How do I setup nhibernate to handle this? - nhibernate

I currently have this code.
tableB = new TableB
{
TableA = tableA,
};
List<TableC> tableC = locations.Select(location => new TableC
{
TableB = tableB
}).ToList();
tableB.TableC = tableC;
tableA.TableB.Add(tableB);
nhibernate.Create(a);
nhibernate.Commit();
The above code works but I find it kinda weird that I have do it like this.
I would like to do something like
tableB = new TableB
{
TableA = tableA,
TableC = MakeAllTableCs()
};
tableA.TableB.Add(tableB);
nhibernate.Create(a);
nhibernate.Commit();
The collection of tableC's is being made in memory and and when I try to do a create I get
not-null property references a null or transient value
It seems that it want's a reference to TableB for each one in the collection of TableC. It seems kinda odd to do this seeing that I am sticking in the TableB object. I would have hoped that it would have figured that out and used it as a reference.
Is there anyway that I can do it so I don't need to have a reference of TableB in each of my TableC objects?
Edit Mapping
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
HasMany(x => x.).Cascade.All().Inverse();
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap ()
{
Id(x => x.Id).GeneratedBy.GuidComb();
References(x => x.TableA).Not.Nullable();
HasMany(x => x.TableC).Cascade.All().Inverse();
}
}
public class TableCMap : ClassMap<TableC>
{
public TableCMap ()
{
Id(x => x.Id).GeneratedBy.GuidComb();
References(x => x.TableB).Not.Nullable();
}
}

Hard to say without your mappings and real NHibernate code (where you call session.Save()) but most likely you are not saving the new TableB objects and also don't have any cascade setting on TableA.b (you need one of those).

Related

Deletion of one-to-zero or one child record not working

I'm trying to map a couple of tables where the primary key in Bar is a foreign key to Foo i.e. a 1..0:1 relationship.
My mappings look like this:
class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("Foo");
Id(x => x.Id).Column("ID");
HasOne(x => x.Bar).Cascade.All();
}
}
class BarMapping : ClassMap<Bar>
{
public BarMapping()
{
Table("Bar");
Id(x => x.FooId).GeneratedBy.Foreign("Foo");
HasOne(x => x.Foo).Constrained();
}
}
The problem is that when I try to remove an instance of Bar by setting Foo.Bar = null and Bar.Foo = null, the record does not get deleted from the database.
What am I missing?
So basicly the problem is that you're using Cascade.All() in the mapping of Foo.
Instead of that you should be using Cascade.AllDeleteOrphan().
Cascade.All
when an object is save/update/delete, check the associations and save/update/delete all the objects found.
Cascade.AllDeleteOrphan
when an object is save/update/delete, check the associations and save/update/delete all the objects found. In additional to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.
There is other dirty solution by making this relation with using HasMany and Cascade.AllDeleteOrphan.
class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("Foo");
Id(x => x.Id).Column("ID");
HasMany(x => x.Bar).KeyColumn("FooId").Cascade.AllDeleteOrphan();
}
}

How to map a string collection from another table in Fluent NHibernate?

I have an entity:
public class Foo
{
public virtual int Id;
public virtual IEnumberable<string> Bars;
}
And its mapping:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foo_table_in_database");
Id(x => x.Id, "Id");
HasMany(x => x.Bars)
.AsList()
.Table("bars_table_in_db")
.Element("BarId", m =>
{
m.Type<string>();
});
}
}
And an exception returned inside the entity insteaf of the expected result :(
base = {"could not initialize a collection: [Loya.Services.CouponsWeb.Promotion.LoyCouponCustomerGroups#2][SQL: SELECT loycouponc0_.Promotion_id as Promotion3_0_, loycouponc0_.LoyCustomerGroupId as LoyCusto1_0_, loycouponc0_.Index as Index0_ FROM loy_promotion__cu...
Database tables :
foo_table : *Id, other properties
bar_table : *FooId, *BarId
My aim is to get a List of BarId's (strings) in my Foo.
How do I map it properly?
I think you might need to specify the KeyColumn. I do something similar in one of my solutions and I would map the entities above as follows...
mapping.HasMany(x => x.Bars)
.Schema("Schema")
.Table("FooBars")
.Element("Bar")
.KeyColumn("FooID")
.ForeignKeyConstraintName("FK_FooBar_Foo")
.Not.Inverse()
.Cascade.All()
.Fetch.Select();
This will give a table called Schema.FooBars. It will have a FooID column (which is a foreign key back to the Foo table) and a Bar column which contains the value in your Bars collection.

NHibernate, adding children via parent vs setting reference to parent on children

I'm currently working on my first NHibernate project.
To test it out I am building an entity graph and that graph I try to persist to the database. When I add children to the parent via the add on the IList the insert is not working in the database because I get null column exceptions on the database (code snippet 1)
When I set a reference on the child to the parent it is working (though this feels not natural for me, see code snippet
Is this normal behavior or am I doing something wrong?
Snippet 1:
var country = new Country();
var countryLocale = new CountryLocale { LangCode = "nl-nl", Name = "Nederland" };
country.CountryLocales.Add(countryLocale);
var city = new City();
var cityLocale = new CityLocale { LangCode = "nl-nl", Name = "Amsterdam" };
city.CityLocales.Add(cityLocale);
country.Cities.Add(city);
Snippet 1 Error:
Cannot insert the value NULL into column 'CountryId', table
'ArtWorld.dbo.City'; column does not allow nulls. INSERT
fails.\r\nThe statement has been terminated.
snippet 2:
var country = new Country();
var countryLocale = new CountryLocale { Country = country,LangCode = "nl-nl",
Name = "Nederland" };
var city = new City{Country = country};
var cityLocale = new CityLocale { City = city, LangCode = "nl-nl",
Name = "Amsterdam" };
session.SaveOrUpdate(country);
City and CityLocale Map:
public class CityMap : ClassMap<City>
{
public CityMap()
{
Table("City");
Id(x => x.Id);
References(x => x.Country).Column("CountryId").Cascade.SaveUpdate();
HasMany(x => x.CityLocales).KeyColumn("CityId").Cascade.SaveUpdate();
}
}
public class CityLocaleMap : ClassMap<CityLocale>
{
public CityLocaleMap()
{
Table("City_Locale");
Id(x => x.Id);
Map(x => x.LangCode).Not.Nullable();
Map(x => x.Name).Not.Nullable();
References(x => x.City).Column("CityId").Cascade.SaveUpdate();
}
}
this is my Country/Countrylocale map:
public class CountryMap : ClassMap<Country>
{
public CountryMap()
{
Table("Country");
Id(x => x.Id);
HasMany(x => x.Cities).KeyColumn("CountryId").Cascade.SaveUpdate();
HasMany(x => x.CountryLocales).KeyColumn("CountryId").Cascade.SaveUpdate();
}
}
public class CountryLocaleMap :ClassMap<CountryLocale>
{
public CountryLocaleMap()
{
Table("Country_Locale");
Id(x => x.Id);
Map(x => x.LangCode).Not.Nullable();
Map(x => x.Name).Not.Nullable();
References(x => x.Country).Column("CountryId").Cascade.SaveUpdate();
}
}
NHibernate Mapping changes :
On the Country side you have
HasMany(x => x.CountryLocales).KeyColumn("CountryId").Cascade.SaveUpdate()
On the CountryLocale side you have
References(x => x.Country).Column("CountryId").Cascade.SaveUpdate();
One of the relations should be marked as inverse using .Inverse().
The .Cascase.SaveUpdate() makes sure that NHibernate manages the life cycle of the child entity CountryLocale when a transient child is added to the parent Country's list of CountryLocales. If you dont want to explicitly handle the life cycle of CountryLocale yourself , I would suggest to mark the Many-To-One side from CountryLocale -> Country as Inverse.
There are two ways you could make your referencing Country in CountryLocale less of a sour eye and more intutive
If Country manages the Locales
Add a method in Country which will manage the Locales which are being added. A client (user of Country and CountryLocale need not explicitly play with the references)
public virtual bool AddCountryLocales(CountryLocale locale)
{
if(!this.CountryLocales.Contains(locale))
{
locale.Country =this;
this.CountryLocales.Add(locale);
return true;
}
return false;
}
CountryLocale is more domain driven
By redefining it such that you cannot have a CountryLocale without a Country
public class CountryLocale
{
public CountryLocale(Country country)
{
this.Country = country;
}
//you need a no-agrument constructor for NHibernate
protected CountryLocale()
{
}
}

NHibernate explicit fluent column mapping

I have a set of fluent object mappings that looks like this:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Map(x => x.Id);
Map(x => x.Status);
}
}
public class SpecialUserMap : SubClassMap<SpecialUser>
{
public SpecialUserMap()
{
Map(x => x.Property);
}
}
public class DirectoryMap : ClassMap<Directory>
{
public DirectoryMap
{
Map(x => x.Id);
HasMany(x => x.SpecialUsers).Where("Status = 0");
}
}
User is a join table, which SpecialUser joins against to get things like status. However, when I try to reference a SpecialUser in Directory's SpecialUsers collection, I get an error of "Undefined column 'Status'", as in the generated SQL, NHibernate tries to grab the Status column from the SpecialUser table, and not the User table.
Is there a way to explicitly tell NHibernate which table to get the Status column in the DirectoryMapping?
The Status property of a User / SpecialUser needs to map onto a single column in the database. You can't have it coming sometimes from User and sometimes from SpecialUser.
As a workaround, you could add a SpecialUserStatus property to SpecialUser, and then you could query on that easily.
That mappings looks right for table-per-subclass mapping, assuming that SpecialUser extends User. My guess is that it's a bug.

Fluent Nhibernate left join

I want to map a class that result in a left outer join and not in an innner join.
My composite user entity is made by one table ("aspnet_users") and an some optional properties in a second table (like FullName in "users").
public class UserMap : ClassMap<User> {
public UserMap() {
Table("aspnet_Users");
Id(x => x.Id, "UserId").GeneratedBy.Guid();
Map(x => x.UserName, "UserName");
Map(x => x.LoweredUserName, "LoweredUserName");
Join("Users",mm=>
{
mm.Map(xx => xx.FullName);
});
}
}
this mapping result in an inner join select so no result come out is second table as no data. I'd like to generate an left join.
Is this possible only at query level?
Try the Optional() method.
Join("Users", m =>
{
m.Optional();
m.Map(x => x.FullName);
});
Only this did work for me (NH 3.3):
Join("OuterJoinTable",
m =>
{
m.Fetch.Join().Optional();
m.KeyColumn("ForeignKeyColumn");
m.Map(t => t.Field, "FieldName");
});