Fluent Nhibernate - mapping a collection of components (value objects)? - nhibernate

I am currently using component maps like this:
public class UserMapping
{
public UserMapping()
{
Id(c => c.Id).GeneratedBy.HiLo("100");
Map(c => c.UserName);
Component(c => c.Country, CountryComponentMapping.Map);
}
}
public sealed class CountryComponentMapping
{
public static void Map(ComponentPart<Country> part)
{
part.Map(x => x.CountryName)
part.Map(x => x.CountryAlpha2)
}
}
I like this becuase I only have to define the mapping for the component/value object in one place.
How would I go about using the same semantics for a collection of the component? (e.g. lets assume we wanted to change this to a collection of countries on the user entity)

You can map this as a Component Collection. Unfortunately there is no overload to HasMany().Component() in Fluent NHibernate that allows you to specify that you want to use a derived class of ComponentMap. You can use a modification of your technique above though.
public sealed class UserMap : ClassMap<User> {
public UserMap() {
Id(c => c.Id).GeneratedBy.HiLo("100");
Map(x => x.Name);
HasMany(x => x.Countries).Component(CountryComponentMapping.Map);
}
}
public sealed class CountryComponentMapping {
public static void Map(CompositeElementBuilder<Country> part) {
part.Map(x => x.CountryName);
part.Map(x => x.CountryAlpha2)
}
}

Related

Fluent NHibernate : Conventions / KeyColumn

Below the code, a Customer can have several address. There is a one-to-many relation. I'd like in the Address table as FK a field named "Customer" and not "Customer_id"
I' tried to add :
.KeyColumn("Customer") > no change
I tried to use to change ForeignKeyConvention no change.
Any idea ?
public class CustomerMap : ClassMap<Customer>
{
protected CustomerMap()
{
Id(x => x.Id).Not.Nullable();
Map(x => x.FirstName).Not.Nullable().Length(25);
Map(x => x.LastName);
HasMany<Address>(x => x.Addresses)
.KeyColumn("Customer")
.AsSet()
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class AddressMap : ClassMap<Address>
{
public AddressMap()
{
Id(x => x.Id);
Map(x => x.City).Not.Nullable().Length(100);
}
}
public class ForeignKeyReferenceConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.PropertyRef("EntityId");
}
}
public void DBCreation()
{
FluentConfiguration config = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("...."))
.Mappings(m =>
m.AutoMappings
.Add(AutoMap.AssemblyOf<Customer>())
.Add(AutoMap.AssemblyOf<Address>()
.Conventions.Setup(c => c.Add<ForeignKeyReferenceConvention>())
)
);
config.ExposeConfiguration(
c => new SchemaExport(c).Execute(true, true, false))
.BuildConfiguration();
}
I've never used automapping myself, but isn't ClassMap only used with "default" fluent mapping (not automapping)? Do you want to use fluent mapping or auto mapping?
Why is your one-to-many inverse although you don't have a many-to-one side mapped?
Btw, what's the purpose of that convention? PropertyRef() shouldn't be used unless absolutely needed (NHibernate can't do some optimizations with that).

Fluent Nhibernate WrongClassException with Polymorphic Parent

I have been using joinedsubclass inheritence mapping for a while and decided to switch to table-per-class because it allows more efficient subtype descrimination. However on making the change I now get the below exception.
Object with id: 1 was not of the specified subclass: MyModel.FooBase (Discriminator was: '2')
Abridged classes are as follows.
public abstract FooBase
{
/* snip other properties */
public virtual string Name { get;set; }
public virtual IList<Child> Children { get; set; }
}
public class Bar : FooBase
{
public virtual string NickName { get;set; }
}
public class Child
{
public virtual FooBase Parent { get;set; }
}
And the mappings
public sealed class ChildMap : ClassMap<Child>
{
public ChildMap()
{
/*snip*/
References(x => x.Parent).ColumnName("ParentId");
}
}
public sealed class FooBaseMap : ClassMap<FooBase>
{
public FooBaseMap()
{
/*snip*/
HasMany(x => x. Children)
.AsBag().Cascade
.AllDeleteOrphan()
.KeyColumnNames.Add("ParentId")
.Inverse();
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
}
}
As it turns out this exception is normally seen on polymorphic collections not specifying a where attribute. Not the case here. I have played around with making the FooBase not abstract, this makes no difference. Didn't think changing mapping strategies would cause any problems other than having to have nullable columns on the subclasses.
The thing thats confusing is that I am not specifying a subclass as this is a polymorphic parent and I am only attempting to access the base properties in this case Name.
Many thanks,
Ok I found the issue as always right after asking the question :(
Anyway it was the mapping using an Enum as the descriminator that was the issue.
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
Types is an enum I used to allow me to not forget the ints used as the descriminator (reason for ints is they are faster than strings in db lookups).
This issue implies you can use Enums directly as descriminators since 3892 so I tried.
DiscriminateSubClassesOnColumn<Types>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
Didn't work so I tried
DiscriminateSubClassesOnColumn<Types>("FooType")
.SubClass<Bar>("Bar", m => m.Map(x => x.NickName))
Also didn't work so I finally settled for the below which does work.
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>((int)Types.Bar, m => m.Map(x => x.NickName))

Override default Fluent NHibernate Column Mapping

I am trying to find the syntax for changing the automap behavior of Fluent NHibernate.
How would I modify the code below to map the UserId property to a column named UserIdentifier (as an example)?
public class MyTypeMap : ClassMap<MyType>
{
public MyTypeMap()
{
Table("MyTypes");
Id(x => x.InstanceId).GeneratedBy.Guid().UnsavedValue(Guid.Empty);
Map(x=> x.UserId);
}
}
Thanks
public class MyTypeMap : ClassMap<MyType>
{
public MyTypeMap()
{
Table("MyTypes");
Id(x => x.InstanceId).GeneratedBy.Guid().UnsavedValue(Guid.Empty);
Map(x=> x.UserId).Column("UserIdentifier");
}
}
public class MyTypeMap : ClassMap<MyType>
{
public MyTypeMap()
{
Id (x => x.InstanceId).Column ("UserIdentifier").GeneratedBy.Guid().UnsavedValue(Guid.Empty);
}
}

Fluent NHibernate table-per-class-hierarchy need to use .Where()?

I have the following mapping for a set of contact classes based off an abstract Contact class implementation.
public class ContactMapping : ClassMap<Contact> {
public ContactMapping() {
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.CreatedDate).Not.Nullable();
Map(x => x.Value).Not.Nullable();
Map(x => x.Level).Not.Nullable();
Map(x => x.Comments);
DiscriminateSubClassesOnColumn("ContactType");
}
}
public class PhoneContactMapping : SubclassMap<PhoneContact> {
public PhoneContactMapping() {
Map(p => p.PhoneType);
DiscriminatorValue("PhoneContact");
}
}
public class EmailContactMapping : SubclassMap<EmailContact> {
public EmailContactMapping() {
DiscriminatorValue("EmailContact");
}
}
public class WebsiteContactMapping : SubclassMap<WebsiteContact> {
public WebsiteContactMapping() {
DiscriminatorValue("WebsiteContact");
}
}
I have an entity class that HasMany EmailContact(s), WebsiteContact(s), and PhoneContact(s).
public class ContactableEntityMapping: ClassMap<ContactableEntity> {
public ContactableEntityMapping() {
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.CreatedDate).Not.Nullable();
Map(x => x.Comment);
HasMany<EmailContact>(x => x.EmailContacts).AsBag().Not.LazyLoad().Where("ContactType='EmailContact'");
HasMany<PhoneContact>(x => x.PhoneContacts).AsBag().Not.LazyLoad().Where("ContactType='PhoneContact'");
HasMany<WebsiteContact>(x => x.WebsiteContacts).Not.LazyLoad().AsBag().Where("ContactType='WebsiteContact'");
HasManyToMany(x => x.Addresses).AsSet();
}
}
If i do not specify there .Where() clauses the class ends up coming back with all rows mapped to EmailContact (since it is first in the listing) if LazyLoading is used, and if lazy-loading is not used I receive an exception as it attempts to cast the classes to the wrong type.
Obviously this is because the SQL executed is not passing in the additional where clause unless I specify it in the mapping. Am I missing something in my mapping, or is the Where mess just something we need to live with?
Thanks for the help!

Subclass of Subclass fluent nHibernate

My model looks like this:
public class SelectionItem : BaseEntity // BaseEntity ==> id, timestamp stuff
{//blabla}
public class Size : SelectionItem
{//blabla}
public class Adultsize : Size
{//blabla}
I would like to use class-hierarchy-per-table-method of fluent nhibernate
public class SelectionItemMap : BaseEntityMap<Entities.SelectionItem.SelectionItem>
{
public SelectionItemMap()
{
Map(x => x.Name);
Map(x => x.Picture);
Map(x => x.Code);
DiscriminateSubClassesOnColumn("SelectionItemType");
}
}
and reset a DiscriminateSubClassesOnColumn on the following subclass:
public class SizeMap : SubclassMap<Size>
{
DiscriminateSubClassesOnColumn("SizeType")
}
public Adultsize : SubclassMap<Adultsize>
{}
But this doesn't work.
I found a solution on the web: link text
but this method is depreciated according to resharper.
How to solve it? thank you for further informations.
When you use class-hierarchy-per-table-method use use one column as discriminator and then define the value of that descriminator for each concrete class.
Try something like this:
public class SelectionItem : ClassMap<SelectionItem>
{
public SelectionItem()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn("SelectionItemType");
}
}
public class Size : SubclassMap<Size>
{
public Size()
{
DiscriminatorValue("Size")
DiscriminateSubClassesOnColumn("SizeType");
}
}
public class Adultsize : SubclassMap<Adultsize>
{
public Adultsize()
{
DiscriminatorValue("Adult")
}
}
This doesn't work, DiscriminateSubClassesOnColumn() isn't available in a SubclassMap.
You don't need to add DiscriminateSubClassesOnColumn() in the SubclassMap. It takes the classname as discriminator value.
Correct version of code
public class SelectionItem : ClassMap<SelectionItem>
{
public SelectionItem()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn("SelectionItemType");
}
}
public class Size : SubclassMap<Size>
{
}
public class Adultsize : SubclassMap<Adultsize>
{
}