add objects with child collections - nhibernate

I have two classes: a Site class and a Phase class. The Site class defines a collection of Phases. Each class corresponds to a database table. The database (SQL Server 2000) has a one to many reference between the two tables such that a given Site can be associated with many Phases but a given Phase can only be associated with a single Site.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class Site
{
public virtual int Id {get; set;}
[Required]
[Editable(true)]
[StringLength(64)]
public virtual string Name { get; set; }
public virtual ICollection<Phase> Phases {get; set;}
}
public class Phase
{
public virtual int Id {get; set;}
[Required]
[Editable(true)]
[StringLength(64)]
public virtual string Name { get; set; }
[Editable(true)]
[StringLength(16)]
public virtual string Code { get; set; }
public virtual int SiteId {get; set;}
public virtual Site Site {get; set;}
}
I'm using FluentNHibernate to do my mapping. I want to map this in such away that I can create a new Site instance, assign a few Phase instances and make one call to get all instances into the database:
Site site = new Site() { Name = "SiteName" };
Phase phase = new Phase() { Name = "PhaseName", Code = "Code" };
Phase otherPhase = new Phase() { Name = "OtherPhaseName" };
site.Phases.Add(phase);
site.Phases.Add(otherPhase);
Session.SaveOrUpdate(site);
I have the following mappings in place, but they aren't doing the trick:
public class SiteMap : ClassMap<Site>
{
public SiteMap()
{
Id(p => p.Id).Column("ST_ID").GeneratedBy.Native();
Map(p => p.Name).Column("ST_Name");
HasMany<Phase>(x => x.Phases).KeyColumn("ST_ID").LazyLoad().Inverse().AsSet();
}
}
public class PhaseMap : ClassMap<Phase>
{
public PhaseMap()
{
Id(p => p.Id).Column("PH_ID").GeneratedBy.Native();
Map(p => p.Name).Column("PH_Name");
Map(p => p.Code).Column("PH_Code").Nullable();
Map(p => p.SiteId).Column("ST_ID");
References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy).Not.Insert().Not.Update();
}
}
I'm new to NHibernate generally, so I recognize there may be other issues with the mappings shown here that I'm not aware of. Any assistance on how best to map these two classes would be appreciated. TIA.

References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy).Not.Insert().Not.Update();
Should just be
References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy);
Your Not Insert and Update specifications do not update this column when you're insert or update the table.
You also need to specify Cascade on your one to many end
HasMany<Phase>(x => x.Phases).KeyColumn("ST_ID").Cascade.AllDeleteOrphan().LazyLoad().Inverse().AsSet();

Related

NHibernate (Fluent) Lazy Loading Not Working

I am attempting to use NHibernate to generate a model for a very odd database. The tables themselves have primary keys for show only, all the actual relationships are on unique columns. For example, a product table with a product id primary key and a unique product name column. Another table, demand, has a product name column and that defines the relationship. I know this situation isn't ideal but it's out of my control.
At any rate, I was able to use Fluent NHibrenate to map product to demand, but I cannot seem to get the entity to lazy-load.
public class Demand
{
public virtual DemandId { get; set; }
public virtual Product { get; set; }
}
public class DemandMap : ClassMap<Demand>
{
public DemandMap()
{
this.Table("Demand");
this.LazyLoad();
this.Id(x => x.DemandId);
this.References(x => x.Product).PropertyRef(x => x.ProductName).LazyLoad();
}
}
Does anyone have any insight into why lazy loading is not working? I know it is not because I can see the product being fetched along with the demand in the SQL profiler.
My idea (Maybe you can try use "HasMany" there is example but you can read something about this):
First class
public class Demand
{
public virtual int DemandId { get; set; }
public virtual int Product { get; set; }
public virtual IEnumerable<NewClass> Name {get; set;}
}
this.HasMany(x=> x.Product).Column("Product_id").not.nullable;
Second class
public class NewClass
{
public virtual Demand Product_id {get; set;}
}
this.References(x => x.Product).Column("product_id).not.nullable

References in fluent hibernate mapping seems dosnt work correctly

I'm using fluent hibernate 1.2.0.712 and nhibernate 3.2.0.4000 in my mvc project as an OR mapper, the problem is :
this is my oJob object:
public class Job{
virtual public Enquiry Enquiry { get; set; }
virtual public long Id { get; set; }
}
and this is Enquiry:
public class Enquiry {
virtual public long Id { get; set; }
}
and here is JobMap:
public class JobMap: ClassMap<Job>
{
public JobMap()
{
Schema("dbo");
Id(p => p.Id)
.Column("Id");
References(p => p.Enquiry);
}
}
i expect that each job has exactly one enquiry
but sometimes that i check sql server i see there are some records in job table with different ids that all have the same enquiryid
and i checked it many times and dont know when exactly it happens, what is the problem?
You must change your mapping : change
References(p => p.Enquiry);
by
HasOne(p => p.Enquiry);

Batch Update, Delete, Insert not work correctly with version control

I'm using Nhibernate 3.2 and fluent nhibernate, I have two tables Customer Group and Customer, and I use for lock management version control with TimeStamp Column.
I have the following classes and maps for these classes:
public class Customer
{
public Customer()
{
}
public virtual int CustomerID { get; set; }
public virtual CustomerGroup customerGroup { get; set; }
public virtual int CustomerGroupID { get; set; }
public virtual string CustomerRef { get; set; }
public virtual string NameE { get; set; }
public virtual string NameA { get; set; }
public virtual byte[] TimeStamp { get; set; }
}
and his map
public class CustomerMap : ClassMap<Customer> {
public CustomerMap() {
Table("Customer");
Id(x => x.CustomerID).GeneratedBy.Identity().Column("CustomerID");
Version(x =>x.TimeStamp).CustomType("BinaryBlob").Generated.Always().Column("TimeStamp");
DynamicUpdate();
OptimisticLock.Version();
References(x =>x.customerGroup).Column("CustomerGroupID").ForeignKey("CustomerGroupID");
Map(x => x.CustomerRef).Column("CustomerRef").Length(30).Unique();
Map(x => x.NameE).Column("NameE").Not.Nullable().Length(100).Unique();
Map(x => x.NameA).Column("NameA").Length(100);
and for Customer Group:
public class CustomerGroup {
public CustomerGroup() {
Customers = new List<Customer>(3);
}
public virtual int CustomerGroupID { get; set; }
public virtual IList<Customer> Customers { get; set; }
public virtual byte[] TimeStamp { get; set; }
}
and his map:
public CustomerGroupMap() {
Table("CustomerGroup");
Version(x => x.TimeStamp).CustomType("BinaryBlob").Generated.Always().Column("TimeStamp");
DynamicUpdate();
OptimisticLock.Version();
Id(x => x.CustomerGroupID).GeneratedBy.Identity().Column("CustomerGroupID");
HasMany(x => x.Customers).KeyColumn("CustomerGroupID");
}
When I create update in list of customers belong to specific Customer Group like this:
ISession Session = OpenSession();
Session.BeginTransaction();
var customerGroupInfo = Session.Query<CustomerGroup>().Fetch(x => x.Customers).Single<CustomerGroup>(x => x.CustomerGroupID == 98);
foreach (var item in customerGroupInfo.Customers)
{
item.NameE = "abc";
Session.Update(item);
}
Session.Transaction.Commit();
apply these sql statements:
UPDATE Customer SET NameE = 'abc'
WHERE CustomerID = 200 AND TimeStamp = 0x00000000000092EF
SELECT customer_.TimeStamp as TimeStamp1_ FROM Customer customer_
WHERE customer_.CustomerID = 200
UPDATE Customer SET NameE = 'abc'
WHERE CustomerID = 201 AND TimeStamp = 0x00000000000092F0
SELECT customer_.TimeStamp as TimeStamp1_ FROM Customer customer_
WHERE customer_.CustomerID = 201
.
.
.
and every update and every select operate in single round trip.
I set property adonet.batch_size property in configuration like this:
<property name="adonet.batch_size">20</property>
I read in this post this behavior founded by default in Nhibernate 3.2.
Any Tips to make batch work correctly?
You might look at changing your Session.FlushMode to something other than Automatic. That way, you could do something like this:
Session.FlushMode = NHibernate.FlushMode.Never
foreach (var item in customerGroupInfo.Customers)
{
item.NameE = "abc";
Session.Update(item);
}
Session.Flush();
Session.Transaction.Commit();
// Perhaps changing the flushmode after commit?
Session.FlushMode = NHibernate.FlushMode.Auto;
Edit :
Nevermind, see this excerpt from the docs: http://nhibernate.info/doc/nh/en/index.html#batch
It appears that batching doesn't get along with optimistic locking.
NHibernate supports batching SQL update commands (INSERT, UPDATE, DELETE) with the following limitations:
.NET Framework 2.0 or above is required,
**the Nhibernate's drive used for your RDBMS may not supports batching,**
since the implementation uses reflection to access members and types in System.Data assembly which are not normally visible, it may not function in environments where necessary permissions are not granted
**optimistic concurrency checking may be impaired since ADO.NET 2.0 does not return the number of rows affected by each statement in the batch, only the total number of rows affected by the batch.**

fluent nHibernate: How to persist a property which is mapped with Formula?

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);
}
}

NHibernate ternary association with multiple values - how to map in a nice way

I've asked a similar question, but I've given up on the idea I had there to solve this problem so I would like some help solving this in a neat way instead.
I've got tables
Image - (Id, Name, RelativeFilePath)
ImageFilter - (Id, Type)
ImageContext - (Id, Name, ...)
ImageContextImage - (Id, ImageContextId, ImageId, ImageFilterId)
Example of data:
ImageContextImage Id ImageContextId ImageId ImageFilterId
1 1 1 1
2 1 1 2
3 2 1 1
4 3 2 1
As you can see, an image in a context can have several filters applied.
All of my entities are very simple, except this mapping of the above. Currently I've got
ImageContext
public virtual int Id
public virtual string Name
public virtual IList<ImageContextImage> Images
ImageContextImage
public virtual int Id
public virtual ImageContext Context
public virtual Image Image
public virtual ImageFilter ImageFilter
The above is very easy to map, but for each image I then get multiple ImageContextImage objects. I would rather have ImageContextImage contain a list of ImageFilter, so that I can simply iterate through that collection. I've tried alot of permutations of AsTernaryAssociation() and it complains that I need a Dictionary, but I want multiple values per key! Any ideas?
Any ideas? Thanks!
Ternary association can be replaced with binary associations but a new entity will appear after the replacement (Removing Ternary relationship types). It is ImageContextImage entity in your case and the hard part is to find best name for such entity. Your example is very close to this replacement.
ImageContextImage entity:
public virtual int Id { get; set; }
public virtual Image Image { get; set; }
public virtual ImageContext Context { get; set; }
public virtual IList<ImageFilter> Filters { get; set; }
and it's mapping:
Id(x => x.Id);
References(x => x.Image);
References(x => x.Context);
HasManyToMany(x => x.Filters); // filters are referenced via many-to-many relation
ImageContext entity:
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<ImageContextImage> ImageContextImageList { get; set; }
and it's mapping:
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.ImageContextImageList)
.Inverse(); // to aggregate all ImageContextImage that are fererencing this instnstance of ImageContext
.KeyColumn("Context_id");
Corresponding database schema is slightly different:
ImageContextImage(Id, Image_id, Context_id)
and new association table must be created for the many-to-many relation:
ImageFilterToImageContextImage(ImageContextImage_id, ImageFilter_id)
Note that this is only a sketch of one possible approach. Many details depends on your problem domain and must be tweaked before it is ready for production:) - e.g.cascades.
I have never used AsTernaryAssociation but it seems interesting. I will investigate it later, thank you for the inspiration:).
EDIT:
Ternary association can be implemented by slightly different mapping (using composite-element) but there is still additional entity - ImageContextImage which is mapped as component in this case:
public class ImageContext
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<ImageContextImage> ImageContextImageList { get; set; }
public ImageContext()
{
ImageContextImageList = new List<ImageContextImage>();
}
}
public class ImageContextMap : ClassMap<ImageContext>
{
public ImageContextMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.ImageContextImageList).Component(c =>
{
c.References(x => x.Image);
c.References(x => x.Filter);
}).Cascade.AllDeleteOrphan();
}
}
public class ImageContextImage
{
public virtual Image Image { get; set; }
public virtual ImageFilter Filter { get; set; }
}
ImageContext class can be extended by these methods to make things simpler:
public virtual IEnumerable<Image> AssociatedImages
{
get
{
return ImageContextImageList.Select(x => x.Image).Distinct().ToList();
}
}
public virtual IEnumerable<ImageFilter> GetFilters(Image image)
{
return ImageContextImageList.Where(x => x.Image == image).Select(x => x.Filter).ToList();
}
No success with AsTernaryAssociation.