I wanted to test whether my entities can be persisted to the database or not, so I came across this article:
http://www.codethinked.com/nhibernate-20-sqlite-and-in-memory-databases
My code to initialize the session factory is the same the one in the article:
public class NHibernateInMemoryTestFixtureBase
{
protected static ISessionFactory sessionFactory;
protected static Configuration configuration;
public static void InitalizeSessionFactory(params Assembly[] assemblies)
{
if (sessionFactory != null)
return;
var properties = new Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SQLite20Driver");
properties.Add("dialect", "NHibernate.Dialect.SQLiteDialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
properties.Add("connection.connection_string", "Data Source=:memory:;Version=3;New=True;");
properties.Add("connection.release_mode", "on_close");
configuration = new Configuration();
configuration.Properties = properties;
foreach (Assembly assembly in assemblies)
{
configuration = configuration.AddAssembly(assembly);
}
sessionFactory = configuration.BuildSessionFactory();
}
public ISession CreateSession()
{
ISession openSession = sessionFactory.OpenSession();
IDbConnection connection = openSession.Connection;
new SchemaExport(configuration).Execute(false, true, false, true, connection, null);
return openSession;
}
}
And here's my test:
[Test]
public void IWillChangeThisNameLater()
{
InitalizeSessionFactory(typeof(LogRepository).Assembly);
var session = this.CreateSession();
Log log = Log.New("a", "b", "I");
session.Save(log);
session.Flush();
Assert.Greater(log.IDColumn, 0);
}
And the problem is, I removed the "a" property of Log from the log.hbm.xml and session.Save(log) is not throwing an exception or anything, it just works...
This must be obvious and on porpose, but I fail to find out why that is, how can it save it if is not mapped, is that how the in memory database work? how can I test my mapping then?
I mainly did this in-memory test so that I can know right away if a valid entity is failing to persist, of course that would include missing properties on the mapping file.
Any thoughts will be appreciated.
EDIT:
As requested,
the Log entity definition:
public class Log : DomainBase<Log, ILogRepository<Log>>
{
private int logId;
private string tableName;
private string field;
private string userLogin;
protected Log()
{
}
protected Log(string tableName, string field, string userLogin)
{
TableName = tableName;
Field = field;
UserLogin = userLogin;
}
public virtual int LogId { get; set; }
public virtual string TableName { get; set; }
public virtual string Field { get; set; }
public virtual string UserLogin { get; set; }
}
the Log Mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DomainProject" table="Log" lazy="true">
<id name="logId" column="ID" type="int">
<generator class="native" />
</id>
<property name="TableName" column="TableName" type="string" />
<property name="Field" column="Field" type="string" />
<property name="UserLogin" column="UserLogin" type="string" />
</class>
</hibernate-mapping>
If a class contains a property that is not mentioned in the mappings, NHibernate will ignore the property.
Related
I want to use mapping by code so I have a class Employee (namespace NHibernateTests.Classes) and a class EmployeeMappings (namespace NHibernateTests.Mappings)
My whole nhibernate configuration is set in an xml file hibernate.cfg.xml which currently goes like this:
<?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.OracleClientDriver</property>
<property name="connection.connection_string">User Id=NHIBERNATE;Password=NHIBERNATE;Data Source=XE</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<mapping assembly="NHibernateTests"/>
</session-factory>
</hibernate-configuration>
Which gives me the runtime error : No persister for: NHibernateTests.Classes.Employee
I tried (and error) some setting for mapping element but with no luck. I read how to set ressource for hbm.xml elements but couldn't find an answer for by code mapping.
namespace NHibernateTests.Classes
{
public class Employee
{
public virtual Address Address { get; set; }
public virtual string FirstName { get; set; }
public virtual int Id { get; set; }
}
}
namespace NHibernateTests.Mappings
{
public class EmployeeMappings : ClassMapping<Employee>
{
public EmployeeMappings()
{
this.Id(e => e.Id, mapper =>
{
mapper.Generator(Generators.HighLow);
});
}
}
}
With a mapping by code you should configure also your factory by code. There is one of few how-to:
NHibernate 3.2 Mapping by Code – Basic Mapping
cited code snippets (see above link for more details)
private static Configuration ConfigureNHibernate()
{
var configure = new Configuration();
configure.SessionFactoryName("BuildIt");
configure.DataBaseIntegration(db =>
{
db.Dialect();
db.Driver();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionStringName = "NH3";
db.Timeout = 10;
// enabled for testing
db.LogFormattedSql = true;
db.LogSqlInConsole = true;
db.AutoCommentSql = true;
});
var mapping = GetMappings();
configure.AddDeserializedMapping(mapping, "NHSchemaTest");
SchemaMetadataUpdater.QuoteTableAndColumns(configure);
return configure;
}
thew way how to get HbmMapping
private static HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.AddMappings(Assembly.GetAssembly(typeof(ProvinceMap)).GetExportedTypes());
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
return mapping;
}
I'm following the NHibernate getting started tutorial: "Your first NHibernate based application". I'm at the point where I create a Product object then use Session.get() to prove I can read the object.
It works fine with SQL Server Ce, but I'm getting an exception when I try to use DB2. (The SQL Server Ce version works - that is. There are some minor changes between the versions like int instead of GUID for the Id.)
I'm very experienced with Hibernate and SQL databases. This is my first experience with NHibernate and DB2. (Coming from the Java world). I'd appreciate any suggestions, especially from the (evidently few) people who are using NHibernate on DB2.
Rob
The full exception is
Test method Examples.DB2.NHibernateExamples.Can_add_new_product threw
exception: NHibernate.Exceptions.GenericADOException: could not load
an entity: [Examples.DB2.Domain.Product#1][SQL: SELECT product0_.Id as
Id1_0_, product0_.Name as Name1_0_, product0_.Category as
Category1_0_, product0_.Discontinued as Disconti4_1_0_ FROM Product
product0_ WHERE product0_.Id=?] ---> System.IndexOutOfRangeException:
Invalid index 0 for this DB2ParameterCollection with Count=0.
This is happening in the Get(...) call in the following code:
[TestInitialize]
public void TestInitialize()
{
TestFixtureSetup();
SetupContext();
}
[TestMethod]
public void Can_add_new_product()
{
var product = new Product { Id = 1, Name = "Apple", Category = "Fruits"};
using (ISession session = _sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
}
using (ISession session = _sessionFactory.OpenSession())
{
//var query = session.CreateQuery("from Product");
//var products = query.List<Product>();
//Assert.AreEqual(1, products.Count);
var fromDb = session.Get<Product>(product.Id);
Assert.IsNotNull(fromDb);
Assert.AreNotSame(product, fromDb);
Assert.AreEqual(product.Name, fromDb.Name);
Assert.AreEqual(product.Category, fromDb.Category);
}
}
private void TestFixtureSetup()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof (Domain.Product).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
}
private void SetupContext()
{
new SchemaExport(_configuration).Execute(true, true, false);
}
The exception seems to indicate that the Id paramter isn't being passed to the DB2 query. If I uncomment the three lines before the Get(), it works fine since the row is already cached and the Get() doesn't actually go to the database.
Here is the definition of Product.cs:
using System;
namespace Examples.DB2.Domain
{
class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Category { get; set; }
public virtual bool Discontinued { get; set; }
}
}
Here is Product.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Examples.DB2"
namespace="Examples.DB2.Domain">
<class name="Product">
<id name="Id">
<generator class="native" />
</id>
<property name="Name" />
<property name="Category" />
<property name="Discontinued" type="YesNo"/>
</class>
</hibernate-mapping>
Here is hibernate.cfg.xml:
<?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="dialect">NHibernate.Dialect.DB2Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.DB2Driver</property>
<property name="connection.connection_string">Database=SAMPLE; UID=DEV; PWD=password</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
I'm working in Visual Studio 2010 Premium on Windows 7. I'm using DB2 Express-C 9.7.4.
Ok,
I found the answer ... not exactly sure of the solution yet, but in the final release of NHibernate they added a call to AdoNet\AbstractBatcher.cs called RemoveUnusedCommandParameters. This procedure calls Driver.RemoveUnusedCommandParameters.
My solution for now is just to comment out this call and let the function do nothing.
I will bring this up with the nhusers group and see if there is a better long term solution.
thanks
dbl
I've been trying all day to get one of my object to be saved with versions but to no avail. Please point out what I'm doing wrong, as I've tried SaveOrUpdate, Merge() and Update() after a Clear() call.
The business object:
public class MappedTest
{
public virtual Guid TestID { get; set; }
public virtual int VersionID { get; set; }
public virtual byte[] Content { get; set;}
public virtual DateTime DateSaved { get; set; }
}
The mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping ...>
<class name="TestImp.Definition.MappedTest, PythonTest" table="Tests">
<id name="TestID" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid"/>
</id>
<version name="VersionID" column="VersionID" />
<property name="Content" column="TestObject" type="BinaryBlob"/>
<property name="DateSaved" column="Date"/>
`
The actual code:
using (var session = new Configuration().Configure().BuildSessionFactory().OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
if(session.Get<MappedTest>(mappedTest.TestID) == null)
{
session.Save(mappedTest);
}
else
{
session.Clear();
session.Update(mappedTest);
}
transaction.Commit();
}
}`
Thanks.
For insert try just with:
using (var session = new Configuration().Configure().BuildSessionFactory().OpenSession())
{
MappedTest mappedTest =new MappedTest();
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(mappedTest);
transaction.Commit();
}
}
for update:
using (var session = new Configuration().Configure().BuildSessionFactory().OpenSession())
{
MappedTest mappedTest =session.Get<MappedTest>(..an Id..);
mappedTest.YourProperty="newValue";
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(mappedTest);
transaction.Commit();
}
}
If you need it try use a session.Flush() to force database operations.
Another possibility that took me some time to realize and is not covered yet on this thread: In such case, check if your mapper is set on ReadOnly. NHibernate does not tell anything when asked to save or update with a ReadOnly mapper.
I have an entity where a composite id is used. I changed to code to make use of wrapping the composite id in a seperate key class. I expected that with Linq I could do a comparison on key object and with the Criteria API to use Restrictions.IdEq but both fail. I need to explicitly compare the key values to make it work.
I cannot find any documentation if this should work so for the moment I am stuck with direct comparisons but this means that when I alter the key that I also need to update the query code which is obviously not what I would want.
As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.
Domain
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
Class
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
Working queries
The queries below work and as you can see I compare the key values explicitly. I do not compare the key object.
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
Expected to work but dont
I expect the following queries to work. Either in efficiently for linq in memory instead of the database but I expect the criteria api to be smart enough to handle such composite id´s in queries.
Both linq and criteria api queries make use of a Key object comparison.
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
So if anyone has such a scenario working then what am I doing wrong?
Not directly an answer to your question, but it may be useful to you anyway. You could avoid the (explicit) composite key by mapping the AddonStatus as composite-element on the owner (most probably the Context):
<class name="Context">
<map name="AddonStates" table="AddonStatus">
<key column="Context_Id" /> <!-- Foreign key to the Context -->
<index column="Addon_Id" /> <!-- Dictionary key -->
<composite-element>
<property name="Status" /> <!-- data -->
</composite-element>
</map>
</class>
In the class Context is looks like this:
class Context
{
IDictionary<AddonType, AddonStatus> AddonStates { get; private set; }
}
This results and pretty the same database structure, but it is different to work with. I can't say if this is what you actually want, but it just looks like it.
Interestingly, I'm getting almost the exact opposite of this behavior in 2.1.2.
My mapping (simplified):
<!-- Subscriber class -->
<class name="Subscriber" >
<composite-id name="SubscriberKey" class="SubscriberKey">
<key-property name="Request" column="RequestID" type="int"/>
<key-many-to-one name="User" column="UserID" class="User" not-found="ignore" />
</composite-id>
<!-- User class - note that this goes to a different schema,
and is not mutable. Who knows if that's important... -->
<class name="User" schema="AnotherDb.dbo" mutable="false">
<id name="Id" column="UserID" type="int">
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="string" />
<property name="LastName" column="LastName" type="string" />
goes to:
public class User
{
public virtual int? Id {get; protected set;}
public virtual string FirstName { get; protected set; }
public virtual string LastName { get; protected set; }
public User() { }
}
public class Subscriber
{
public virtual SubscriberKey SubscriberKey { get; set; }
public virtual User User { get; set; }
public Subscriber() { }
}
public class SubscriberKey
{
public override bool Equals(object obj)
{
if (obj is SubscriberKey && obj != null)
return ((SubscriberKey)obj).Request == Request
&& ((SubscriberKey)obj).User.Id == User.Id;
return false;
}
public override int GetHashCode()
{
return (Request.ToString() + User.Id.ToString()).GetHashCode();
}
public virtual int Request { get; set; }
public virtual User User { get; set; }
public SubscriberKey() { }
}
Things which work:
CreateCriteria<Subscriber>()
.Add(Restrictions.IdEq(keyInstance))
.UniqueResult<Subscriber>();
CreateCriteria<Subscriber>()
.Add(Restrictions.Eq("SubscriberKey.User.Id", aUserID))
.Add(Restrictions.Eq("SubscriberKey.Request", aRequestID))
.UniqueResult<Subscriber>();
Things which don't work:
Get<Subscriber>(keyInstance);
I'm thinking this is an inconsistency between their various ID-equaling query forms. When I get time, I'll be building a minimal unit test to submit as a bug example. I'd be interested in any / all thoughts anyone might have on this...
edit: Heeey, I figured it out!
Things which do work, now that I've read this
Get<Subscriber>(new SubscriberKey() {
User = Load<User>(aUserID), // the important part!
Request = aRequestID
});
This will create a proxy object for the User key, without hitting the database (unless necessary). If you swap Load<User> for Get<User>, you'll immediately hit the database to populate the object, rather than respecting your lazy-loading properties. Use Load.
And things like this are precisely why people suggest the (type)Repository pattern - I can do this behind the scenes: Get<>(new SK(){User=Load<>(key.User.Id)}, and still Get(key) by a single key, identical to every other object.
I have the following database schema:
http://lh4.ggpht.com/_SDci0Pf3tzU/SdM3XnAmmxI/AAAAAAAAEps/Ie3xW3ZVNfQ/s400/styleerror.png
And this is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
<class name="CodeSmithSampel.Generated.BusinessObjects.Store, CodeSmithSampel" table="store" lazy="true">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<bag name="Employees" lazy="true" cascade="all-delete-orphan" inverse="true" >
<key column="Store_id"></key>
<one-to-many class="Employee"></one-to-many>
</bag>
<bag name="Products" table="storeproduct" lazy="true" cascade="all" inverse="true" >
<key column="Store_id"></key>
<many-to-many column="Product_id" class="Product" />
</bag>
</class>
</hibernate-mapping>
And ths is my Store entity class:
public partial class Store : BusinessBase<int>
{
#region Declarations
private string _name = String.Empty;
private IList<Employee> _employees = new List<Employee>();
private IList<Product> _products = new List<Product>();
#endregion
#region Constructors
public Store() { }
#endregion
#region Methods
public override int GetHashCode()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().FullName);
sb.Append(_name);
return sb.ToString().GetHashCode();
}
#endregion
#region Properties
public virtual string Name
{
get { return _name; }
set
{
OnNameChanging();
_name = value;
OnNameChanged();
}
}
partial void OnNameChanging();
partial void OnNameChanged();
public virtual IList<Employee> Employees
{
get { return _employees; }
set
{
OnEmployeesChanging();
_employees = value;
OnEmployeesChanged();
}
}
partial void OnEmployeesChanging();
partial void OnEmployeesChanged();
public virtual IList<Product> Products
{
get { return _products; }
set
{
OnProductsChanging();
_products = value;
OnProductsChanged();
}
}
partial void OnProductsChanging();
partial void OnProductsChanged();
#endregion
}
The product class:
public partial class Product : BusinessBase<int>
{
#region Declarations
private float _price = default(Single);
private string _name = null;
private IList<Store> _stores = new List<Store>();
#endregion
#region Constructors
public Product() { }
#endregion
#region Methods
public override int GetHashCode()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().FullName);
sb.Append(_price);
sb.Append(_name);
return sb.ToString().GetHashCode();
}
#endregion
#region Properties
public virtual float Price
{
get { return _price; }
set
{
OnPriceChanging();
_price = value;
OnPriceChanged();
}
}
partial void OnPriceChanging();
partial void OnPriceChanged();
public virtual string Name
{
get { return _name; }
set
{
OnNameChanging();
_name = value;
OnNameChanged();
}
}
partial void OnNameChanging();
partial void OnNameChanged();
public virtual IList<Store> Stores
{
get { return _stores; }
set
{
OnStoresChanging();
_stores = value;
OnStoresChanged();
}
}
partial void OnStoresChanging();
partial void OnStoresChanged();
#endregion
}
The mapping for the Product class:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
<class name="CodeSmithSampel.Generated.BusinessObjects.Product, CodeSmithSampel" table="product" lazy="true">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Price" column="Price" />
<property name="Name" column="Name" />
<bag name="Stores" table="storeproduct" lazy="true" cascade="all" inverse="true" >
<key column="Product_id"></key>
<many-to-many column="Store_id" class="Store" />
</bag>
</class>
</hibernate-mapping>
What is particularly weird is that when I add a Store object to one of the product, the database record is not updated; the add doesn't seem to take place, although the new store object exists in the database:
IManagerFactory managerFactory = new ManagerFactory();
var productManager = managerFactory.GetProductManager();
var myProduct= productManager.GetById(2);
var myStore = new Store();
myStore.Name = "new Store"; //a "new store" entry is created in the Store table
myProduct.Stores.Add(myStore); // but this "new store" is not linked to the myproduct, as it should.
productManager.Session.CommitChanges();
Is there anything I miss?
Note: I generate the above code using CodeSmith.
Edit: The accepted answer works. The reason I got in this problem is because
Only one entity class should have inverse = true, not two. So either Product or Store should set the inverse to false. The code generation tool didn't handle this properly.
The correct way to Add Many to Many relationship is explained below. You must add two times.
Can this have anything to do with the fact that you have a surrogate key in the storeproducts table ?
What happens if you remove this surrogate key column Id, and put the primary key on the combination of the product_id and store_id columns ?
I believe that, if you want to have a surrogate key on the storeproducts table, you'll have to create yet another entity.
If you want to use the surrogate key, you'll have to use the idbag mapping.
How does your Product class and mapping look like ?
I see that you specify the 'inverse' attribute in your mapping of the Products collection in the Store entity.
If you do this (and thus you have a bi-directional association), then you should add the Store to the Stores collection of the product as well.
Since -from the NH documentation- :
Changes made only to the inverse end
of the association are not persisted.
This means that NHibernate has two
representations in memory for every
bidirectional association, one link
from A to B and another link from B to
A. This is easier to understand if you
think about the .NET object model and
how we create a many-to-many
relationship in C#:
category.Items.Add(item); // The category now "knows" about the relationship
item.Categories.Add(category); // The item now "knows" about the relationship
session.Update(item); // No effect, nothing will be saved!
session.Update(category); // The relationship will be saved
The non-inverse side is used to save
the in-memory representation to the
database. We would get an unneccessary
INSERT/UPDATE and probably even a
foreign key violation if both would
trigger changes! The same is of course
also true for bidirectional
one-to-many associations.
You may map a bidirectional
one-to-many association by mapping a
one-to-many association to the same
table column(s) as a many-to-one
association and declaring the
many-valued end inverse="true".
This means, that only one of the ends should be inverse.
Adding a Product to a store, should be done like this:
public class Store
{
public void AddProduct( Product p )
{
if( _products.Contains (p) == false )
{
_products.Add (p);
p.AddStore(this);
}
}
}
public class Product
{
public void AddStore( Store s )
{
if( _stores.Contains (s) == false )
{
_stores.Add (s);
s.AddProduct(this);
}
}
}
(Very important to check whether the collection already contains the item to be added; otherwise you'll end up in an infinite loop.