I'm trying to adopt Fluent NHibernate with my project, currently I can get data from database, when I'm at application server, data is include its PK but when I return this data (as List) to client all of its PK is loose.
How can I fixed this problem?
Update
My POCO class is below: PKs are CountryCd and CityCd
public class coCity
{
public virtual string CountryCd { get; private set; }
public virtual string CityCd { get; private set; }
public virtual string CityNameTH { get; set; }
public virtual string CityNameEN { get; set; }
public virtual int DeliveryLeadTime { get; set; }
public virtual string CreateBy { get; set; }
public virtual DateTime CreateDate { get; set; }
public virtual string UpdateBy { get; set; }
public virtual DateTime UpdateDate { get; set; }
public override bool Equals(object obj)
{
return this.GetHashCode().Equals(obj.GetHashCode());
}
public override int GetHashCode()
{
return (this.CountryCd + this.CityCd).GetHashCode();
}
}
Mapping class:
public class coCityMap : ClassMap<coCity>
{
public coCityMap()
{
Table("coCity"); // this is optional
CompositeId()
.KeyProperty(x => x.CountryCd)
.KeyProperty(x => x.CityCd);
Map(x => x.CityNameTH);
Map(x => x.CityNameEN);
Map(x => x.DeliveryLeadTime);
Map(x => x.CreateBy);
Map(x => x.CreateDate);
Map(x => x.UpdateBy);
Map(x => x.UpdateDate);
}
}
Source code to get data at application server
public List<coCity> GetTest()
{
List<coCity> result = new List<coCity>();
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
result = (List<coCity>)session.CreateCriteria(typeof(coCity)).List<coCity>();
}
return result;
}
When its still at application server data is retrieve correctly as image below
alt text http://img138.imageshack.us/img138/1071/serverside.png
However when this data transit back to client side all of its PKs is loose like below.
alt text http://img203.imageshack.us/img203/1664/clientside.png
First of all, this isn't a problem with Fluent NHibernate so:
Serializable must be used on your POCO's when you serialize them.
(from your comment) NHibernate keeps a reference of the object retrieved from the database to a cache (1-st level cache). While you serialize this 'managed' object the output of the serialization is an unmanaged object. Nhibernate does not detect that a an object exists in the db just because you set an value in a newly constructed object. You must get the object from the database and update its properties and call Update() or you work with pure sql with the object that returned from the client (yikes!).
Note that is irrelevant with this question: your Equals() implementation is really bad as it doesn't take into account types and depends only on GetHashCode value. If all your classes have this implementation you could run into trouble.
I think the problem is with that private setter on the PK's properties. Try changing that to public.
Either way, mark your entity with Serializable
A few comments:
As a general recomendation when using nhibernate is to avoid composite Ids. Create on your model a surrogate Id that is an identity column and enforce uniqueness of CityCd and CountryCd somewhere else
When passing data around client/server tiers, consider using DTOs to avoid some commong LazyInitializationExceptions problems.
Related
I'm having trouble persisting primitive type collection using (Fluent)NHibernate.
Here's the entity and mapping:
public class SomeOne
{
public virtual long ID { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Iesi.Collections.Generic.ISet<string> Foo { get; protected set; }
public SomeOne()
{
Foo = new HashedSet<string>();
}
}
public SomeOneMap()
{
Id(x => x.ID).GeneratedBy.Identity();
Map(x => x.Name);
Map(x => x.Description);
HasMany(x => x.Foo).Element("Code").AsSet().Not.Inverse();
Table("SomeTypeOne");
}
However, when I try to save SomeOne instance, associated Foo strings get's ignored.
var session = factory.OpenSession();
var one = new SomeOne();
one.Foo.Add("Dato");
one.Foo.Add("Mari");
session.Save(one);
Any idea what could be wrong?
Thanks
UPDATE
Here's db schema. it's generated by NH.
There are two ways of ensuring your collected is persisted.
Call session.Flush(); after session.Save(one);. This will cause NHibernate to persist your collection. More information on flush can be found here.
Wrap the whole thing in a transaction, i.e.
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var one = new SomeOne();
one.Foo.Add("Dato");
one.Foo.Add("Mari");
session.Save(one);
transaction.Commit();
}
There are several reasons why option two is better than option one. The main advantage is that all objects are saved back to the DB within the same transaction, so if one insert fails then the transaction is rolled back and your DB is not left in an inconsistent state. The acceped answer to this question has an extensive explanation of why you should use transactions with NHibernate.
I'm converting from Fluent to Loquacious, and I've run in to an issue where my interceptors are not getting all the fields like I think they should. If I look at the OnSave function
public override Boolean OnSave(Object entity, Object id, Object[] state,
String[] propertyNames, IType[] types)
and take a look at the propertyNames the only items in there are the items that were explicitly mapped in the mapping file (in the example this would just be ID, Start, and End).
In my case though I have a base class which isn't mapped at all. Instead it's just contains properties that get filled out by the interceptors. This used to work in Fluent Nhibernate, but now that I've moved to Nhibernate 3.3 I can't get it to work anymore.
My classes/mapping look something like this
public class BaseAuditEntity
{
public virtual int ModifiedByUserID { get; set; }
public virtual DateTime LastModifiedTime { get; set; }
}
public class Foo : BaseAuditEntity
{
public virtual int ID { get; protected internal set; }
public virtual DateTime Start { get; protected internal set; }
public virtual DateTime End { get; protected internal set; }
}
public class FooMap: ClassMapping<Foo>
{
Id(x => x.ID, m => m.column("fooID"));
Property(x => x.Start, m => m.column("start"));
Property(x => x.End, m => m.column("end"));
}
Any ideas of how to get this work? I don't want to have to map this every class, and I didn't think I needed to map the BaseAuditEntity, at least with Fluent it wasn't needed.
you could make a base mapping class
public class BaseAuditEntityMapping<T> : ClassMapping<T> where T: BaseAuditEntity
{
ManyToOne(x => x.ModifiedByUser);
Property(x => x.LastModifiedTime);
}
public class FooMap: BaseAuditEntityMapping<Foo>
After reading some of the articles about Fluent NHibernate I got confused from where to start
I have an existing database to which I need to create DataAccessLayer. I am new to NHibernate and FluentNhibernate. Since I understood that there is no need to write hbm.xml files, I picked Fluent Nhibernate.
So, What is FluentMapping? and AutoMapping?
I have created a classLibraryProject named FirstProject.Entities
I have created a class named "Customer"
namespace FirstProject.Entities
{
public class Customer
{
public virtual int CustomerID { get; set; }
public virtual string CustomerName { get; set; }
public virtual string Address1 { get; set; }
public virtual string Address2 { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual int Zip { get; set; }
}
}
Then I created a Mapping class
namespace FirstProject.Entities
{
public class CusotmerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID).Column("CustomerID").GeneratedBy.Assigned();
Map(x => x.CustomerName);
Map(x => x.Address1);
Map(x => x.Address2);
Map(x => x.City);
Map(x => x.Zip);
}
}
}
I now don't know how to proceed further. Am I doing it right.. please suggest
how to configure and proceed further
The following is Fluent
Id(x => x.CustomerID).Column("CustomerID").GeneratedBy.Assigned();
I use Fluent assertions, like the following
actual.Should().BeGreaterThan(1).And().LessThan(2);
Fluent is basically where you chain together the commands such that it reads quite well.
Auto mapping is where you do nothing. Everything is done by conventions. I tend to use Auto. Fluent is nice if you don't follow conventions.
Based on your mapping, the CustomerId being Assigned is not the out-of-the-box convention. As such you need to either
Use Fluent to specify exactly how it should map. This is just like doing it the standard way in XML, but with a fluent interface.
Use Auto and specify a Convention that will automatically change CustomerId to be Assigned.
Use Auto and specify an Override, that will use Auto but override CustomerId to be Assigned.
If you want to do option 3, here is the code:
var model = AutoMap
.AssemblyOf<Customer>()
.Where(IsMapped)
.Override<Customer>(a => a.Id(b => b.CustomerId, "CustomerId").GeneratedBy.Assigned());
The function IsMapped must return True for entities you want to Map.
I am dealing with a legacy database, and we have a field which doesn't make sense anymore, but I would rather not change the DB schema.
I'm trying to map an old DB text field into a class with a boolean (only need to know about one option that the DB text field has). I can get the boolean value out of the DB using Forumla, but I can seem to get it to save any updates back into the DB.
My class and current fluent mapping for it is:
public class Bulletin
{
public virtual int Id { get; set;}
public virtual bool RegularBulletin { get; set;}
}
public class BulletinMapping : ClassMap<Bulletin>
{
public BulletinMapping()
{
Table("Bulletins");
Id(x => x.Id, "ID").GeneratedBy.Identity();
Map(x => x.RegularBulletin)
.Formula("case when EmailType = 'BULLETIN_B' then 1 else null end")
.Nullable();
}
}
Does anyone have any ideas about how to persist the RegularBulletin field?
Thanks
Saan
I would use a workaround for this- create a backing field protected virtual string RegularBulletinString and use your boolean conversion formula on it.
public class Bulletin
{
public virtual int Id { get; set;}
protected virtual string RegularBulletinString { get; set;}
public virtual bool RegularBulletin
{
get
{
return RegularBulletinString == "BULLETIN_B";
}
set
{
RegularBulletinString = value? "BULLETIN_B" : null;
}
}
}
public class BulletinMapping : ClassMap<Bulletin>
{
public BulletinMapping()
{
Table("Bulletins");
Id(x => x.Id, "ID").GeneratedBy.Identity();
Map(x => x.RegularBulletinString)
.Column("EmailType")
.Nullable();
IgnoreProperty(x=> x.RegularBulletin);
}
}
I need to persist this class on database using Fluent NHibernate:
public class RaccoonCity
{
public virtual int Id { get; private set; }
public virtual DateTime InfectionStart { get; private set; }
private IList<Zombie> _zombies = new List<Zombie>();
public virtual IEnumerable<Zombie> Zombies
{
get { return _zombies; }
}
protected RaccoonCity()
{}
public RaccoonCity(DateTime startMonth)
{
InfectionStart = startMonth;
}
public virtual void AddZombie(Zombie z)
{
_zombies.Add(z);
}
}
The property has type IEnumerable to indicate that you shouldn´t use it to insert new items. The backing field is of IList to make it easy to insert new items from the own class.
Zombie is a simple class:
public class Zombie
{
public virtual int Id { get; private set; }
public virtual string FormerName { get; set; }
public virtual DateTime Infected { get; set; }
}
The map is the following:
public class RaccoonCityMap: ClassMap<RaccoonCity>
{
public RaccoonCityMap()
{
Id(x => x.Id);
Map(x => x.InfectionStart);
HasMany(x => x.Zombies)
.Access.CamelCaseField(Prefix.Underscore)
.Inverse()
.Cascade.All();
}
}
When I test this, the data is inserted in database, but the zombie´s foreign keys are empty, and the RaccoonCity instance has zero items on Zombies list.
You are declaring the relationship as Inverse, which means the Zombie and not the RacoonCity is responsible for maintaining the relationship.
Either add the corresponding reference to zombie and set it on the AddZombie method, or remove the Inverse (in that case, you'll see an INSERT with a null FK followed by an update).
Suggested reading: http://nhibernate.info/doc/nh/en/index.html#collections-onetomany
Found a post about it: https://web.archive.org/web/20090831052429/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/08/15/a-fluent-interface-to-nhibernate-part-3-mapping.aspx
I had to implement the method
HasManyComponent by myself since it
was missing in the actual trunk of the
framework. That is, it was not
possible to map a collection of value
objects. But it has not been that hard
since the source base is really nice.
My changes will probably be integrated
into the framework soon.
And this one:
http://nhforge.org/blogs/nhibernate/archive/2008/09/06/a-fluent-interface-to-nhibernate-part-3-mapping-relations.aspx