Fluent nHibernate: why not use lambda expressions in Column() - fluent-nhibernate

If I want my object to reference a "foo" object using the "foo_key" field I code it like this:
References(x => x.foo)
.Column("foo_key");
My problem with this is that I much prefer the compile-time safety of fluent, and I hate seeing that hard-coded string "foo_key", when I know that there is a property of that same name. I would like instead to use this:
References(x => x.foo)
.Column(x=> x.foo.foo_key);
Am I missing something in Fluent nHibernate? Should this not be possible, and strongly desired?

Column is not a property, it is the name of the column in the database. It must be a string.

Related

How can I make a where clause on a reference in fluent-nhibernate mapping

There exists such a method on HasMany and HasManyToMany but for some reason there isn't such a mechanism on References.
We have an object that references an other object that can be updated and saved as new versions but from our child object we doesn't really care we only want to load the latest version of the related object. The mapping cannot use the primary key for the related object since this will change for each version of the object so instead we want to map the related object to an property that doesn't change between versions and then make a where-clause to only selected the matching element with the highest version.
So our mapping is like the following
References(p => p.RelatedObjectIdentifier).PropertyRef("MatchingPropIdentifier").Not.Nullable;
We would like to do something like this
References(p => p.RelatedObjectIdentifier).PropertyRef("MatchingPropIdentifier").Where(p => p.IsLatest).Not.Nullable;
Of course we would update the property IsLatest (bool property) for each saving of the related object.
So since the Where(p => p.IsLatest) doesn't exist on a References for a classmap/subclassmap how can we make this happen in any other way?
You can only use the Where() restriction, which:
Defines a SQL 'where' clause used when retrieving objects of this type.
on the class map of the referenced type.
Example:
public class RelatedObjectMap : ClassMap<RelatedObject>
{
public RelatedObjectMap()
{
Table("RelatedObjectTable");
//Id() etc.
Where("MatchingPropIdentifier IS NOT NULL)");
}
}

NHibernate: why field.camelcase?

Can someone tell me why in NHibernate mapping we can set access="field.camelcase", since we have access="field" and access="property"?
EDIT: my question is "why we can do this", not "what does it mean". I think this can be source of error for developper.
I guess you wonder what use field.camelcase have when we can do the same with just field? That's true, but that would give (NH) properties unintuive names when eg writing queries or reference the property from other mappings.
Let's say you have something you want to map using the field, eg
private string _name;
public string Name { get { return _name; } }
You sure can map the field using "field" but then you would have to write "_name" when eg writing HQL queries.
select a from Foo a where a._name = ...
If you instead using field.camelcase the data, the same query would look like
select a from Foo a where a.Name...
EDIT
I now saw you wrote "field.camelcase" but my answer is about "field.camelcase-underscore". The principles are the same and I guess you get the point ;)
the portion after the '.' is the so called naming strategy, that you should specify when the name you write in the hbm differ from the backing field. In the case of field.camelcase you are allowed to write CustomerName in the hbm, and NHibernate would look for a field with name customerName in the class. The reason for that is NHibernate not forcing you to choose a name convention to be compliant, NH will works with almost any naming convention.
There are cases where the properties are not suitable for NH to set values.
They may
have no setter at all
call validation on the data that is set, which is not used when loading from the database
do some other stuff that is only used when the value is changed by the business logic (eg. set other properties)
convert the value in some way, which would cause NH performing unnecessary updates.
Then you don't want NH to call the property setter. Instead of mapping the field, you still map the property, but tell NH to use the field when reading / writing the value. Roger has a good explanation why mapping the property is a good thing.

Converting one Func delegate to another for use with a generic repository

My aim is to achieve something like this:
IList<People> peeps = _peopleRepository.Get<People>(x.Name == "dan");
//signature looks like this: IList<T> Get<T>(Func<T, bool> query) where T : IEntity;
This would be doing this internally:
_sessionFactory.GetCurrentSession().QueryOver<People>(x.Name=="dan").List();
//NHibernate.IQueryOver<T,T> QueryOver<T>(System.Linq.Expressions.Expression<Func<T>> alias)
where T : class
This issue I have is converting my 'query' to the 'alias' as the types are obviously different. Is this a futile task? Is there away of achieving what I am aiming for?
Although I don't use NHibernate, it seems as though your type signatures are different. Your Get method should be accepting a parameter of type System.Linq.Expressions.Expression<Func<T, bool>>. This way you can pass it to the QueryOver method which accepts a parameter of the same type.

Fluent NHibernate: How do you change the underlying sql type for an automapped collection of strings?

I have a class:
public class ParentClass
{
//other stuff
IList<string> ChildPages
}
When 'IList<string> ChildPages' gets automapped by Fluent NHibernate a 'ChildPages' join table is created with a nvarchar(255) backing field for each string in the collection.
But the problem is that I want the sql backing field to be 'text', so that I can have lengthy entries for this entity.
What's the easiest way to make this change?
How do you change the underlying type of automapped primitive collections?
Also, for extra points, how would you do this with a convention or mapping overrides?
Thx!
You can setup an override convention so that strings use text instead of nvarchar2 (255), as shown in the solution on this other thread. If you only want such behavior for a specific property, something like this in your fluent configuration would work:
AutoMap
.AssemblyOf<ParentClass>()
.Override<ChildPage>(map =>
{
mapping.Map(x => x.Page).CustomType("StringClob").CustomSqlType("text");
});

Enum to integer mapping causing updates on every flush

I am trying to map an enum property (instance of System.DayOfWeek) in my model to an integer database field.
Other enum properties in the model should be mapped to strings, so I don't wish to define a convention.
I understand that this should be possible using a fluent mapping like:
Map(x => x.DayOfWeek).CustomType<int>();
and indeed, at first glance this appears to be working.
However, I noticed that instances of entities with properties mapped in this way are being updated every time the session was flushed, even though no amendments have been made to them.
To find out what is causing this flush, I set up an IPreUpdateEventListener, and inspected the OldState and State of the entity.
See the attached image. In the OldState, the relevant object is an int, whereas in State it is a DayOfWeek.
If I use an HBM XML mapping with no type attribute specified, this issue doesn't arise.
So...
Is this a bug or shortcoming in the GenericEnumMapper?
Is there any way of telling the FNH mapping not to specify any type attribute on the generated HBM?
If not, can I specify the default type that NH uses for enums (and what is that)?
If you use my enum convention you don't have that problem.
public class EnumConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType == typeof(AddressType) ||
x.Property.PropertyType == typeof(Status) ||
x.Property.PropertyType == typeof(DayOfWeek));
}
}
You can then map your property like regular:
Map(x => x.DayOfWeek);
EDIT : Updated the convention to pick specific enums to use for the int conversion. All enums that are not checked here would be mapped as string. You might have to experiment a little with what to actually test against. I am unsure if the propertytype will do it directly.
I know I'm late to the party - it's been two years since the question. But since I've stumbled upon this, I might just add solved the problem for me:
Map(x => x.DayOfWeek).CustomType<enumType>();
It did the trick for me: it stopped updating every time.
Source: https://groups.google.com/forum/#!searchin/fluent-nhibernate/enum/fluent-nhibernate/bBXlDRvphDw/AFnYs9ei7O0J
One workaround that I use is to have an int backing field and letting NHibernate use that for mapping.
Whenever NHibernate has to do a cast to compare the new value with the old one - it is always marked as dirty - causing the flush.
Simple way that worked for me was to change mapping's custom type from int to PersistentEnumType. Make sure to declare a generic version to make your life easier:
public class PersistentEnumType<T> : PersistentEnumType {
public PersistentEnumType() : base(typeof(T)) {}
}
Then use
Map(x => x.DayOfWeek)
.CustomType<PersistentEnumType<System.DayOfWeek>>();
This does not require changes to your entities, only mappings, and can be applied on a per-property basis.
See more here.
It depends on if you need to have DayOfWeek specifically as an integer.
If you're casting as part of the mapping, equality will always fail, and the property will be marked as dirty.
I'd probably map:
Map(x => x.DayOfWeek).CustomType();
and create a read only property that presents the DayOfWeek value as an integer if it's really required. Regardless, mapping as the actual type should work and prevent false-dirty.
You might consider an alternate approach; I have found the usage of Fabio Maulo's well known instance types to be invaluable for such uses. The benefit of these is immediately apparent any time you find yourself trying to extend what a basic enum can do (eg. providing a localized description, etc.)