Fluent Nhibernate - Generate a non-primary-key sequence generated value - nhibernate

I have an entity mapping quite similar to this one.
public class MyClassMap : ClassMap<MyClass>
{
public MyClassMap()
{
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.Description);
}
}
I'd like to know if there's any possible way to have the Code field (which is not part of the Primary Key) autogenerated by a sequence. There's a GeneratedBy property, but it's only an IdentityPart class member.

I don't see how using Listeners makes it any easier to use a built-in method for using sequence generators for non-ID columns.
However, if the only solution is to hook into OnPreInsert, making a direct query to the DB & invoke the sequence and get its value, then I suppose I'll have to live with it.
Is this how you solved the issue, Mauro?
Edit:
posted the question on the nHibernate & FluentNHibernate google groups:
https://groups.google.com/group/nhusers/browse_thread/thread/35d37b9abf3566f0
https://groups.google.com/group/fluent-nhibernate/browse_thread/thread/35d37b9abf3566f0

You need to use SaveOrUpdateEventListeners. See here to see Jake's reply for how to get it working for Fluent.

Related

NHibernate mapping reference and reference id

I have simple task. I need map reference to properity and their ID to other properity.
If I use this map I get error:
"Additional information: Invalid index 24 for this
SqlParameterCollection with Count=24."
public CityMap()
{
Map(x => x.Name).Not.Nullable();
//Map(x => x.AddressID); //This line makes trouble
References(x => x.Address);
We can do it for read operation (I am quite happy to use this approach). Just, one of these properties must be insert="false" and update="false".
In fluent it should be like this:
...
Map(x => x.AddressID)
.Insert(false).Udpate(false)
// or just
// .ReadOnly()
;
References(x => x.Address);
We just have to decide which will be the readonly. Reference? or ValueType? I would make Reference editable, because then all NHibernate built in features will work (e.g. assign to transient object will properly later insert the just created ID)
Also check these for some more details check this or here.

NHibernate fluent Prevent children being updated

The application I have uses localization. The way it is built, is that it examines an entity (traversing down the structure) and translates every property marked as 'translate'.
The translations are stored in separate translation tables.
This is all fine but it leaves me with the problem that I now get the translated values in my 'default' values when I update the entity using the translations. And I don't want that.
Let me try and explain better.
Database:
The mapping of footprintlinevLue:
public class FootprintLineValueMap : ClassMap<FootprintLineValue> {
public FootprintLineValueMap() {
Table("FootprintLineValue");
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
References(x => x.FootprintLine).Column("FootprintLineId");
References(x => x.CategoryValue).Column("CategoryValueId").Cascade.None();
}
As you can see a footprintline has multiple values that reference a categoryValue. The categoryvalue is localized.
When I now retrieve footprintlines our framework will put it through our translationservice and will automatically translate the Name and Description of the CategoryValue in the corresponding culture. If it cant find a translation in CategoryValueLocal, it will use the default in CategoryValue.
However...if I save a Footprintline, it will save the translated values back into CategoryValue (overwriting the default) instead of ignore it.
CategoryValues are not value objects and could be changed so I cant make them readonly.
I tried to map the reference as Cascade.None, but that doesn't seem to do anything.
I hope there is a way to simply mark this in the mapping so we can keep on using our TranslationService instead of having to figure out another way to hande localization.
mark the properties as not updateable.
Map(x => x.Description).Not.Update();
you could even define a convention to do so
class TranslatedPropertiesConvention : AttributePropertyConvention<Translated>
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.Not.Update();
}
}

Version does not increment when updating child item

If I read the documentation correctly if I have an Order entity mapped with a version column (incremented by nhibernate) then changes to orderlines should update the version number for the aggregate root (the Order). It does indeed do this when I add/remove orderlines, but if I only change, for example the quantity on an orderline the version of the order is not updated. Is this expected behavior?
I checked the NH source and it appears it only checks for dirty collections when trying to determine if version increment is necessary, and the collection will only be dirty when adding/removing items, not if any item in the collection is dirty.
I have the following mapping:
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Id(c => c.Id).GeneratedBy.GuidComb();
Version(c => c.Version);
OptimisticLock.Version();
HasMany(x => x.OrderLines)
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class OrderLineMap : ClassMap<OrderLine>
{
public OrderLineMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Quantity);
References(x => x.Order);
}
}
So my question is if this is expected behavior? That is that version will not update when modifying child entities only when the child collection is modified with remove/add. It sort of breaks the aggregate root concurrency model.
This is indeed expected behavior. There is a way to solve it using a event listener that can detect changes in children and traverse to the aggregate root and lock that optimistically (triggering a version change).
More information in this post by Ayende:
http://ayende.com/blog/4055/nhibernate-automatic-change-tracking-for-aggregate-roots-in-ddd-scenarios

Automapping doesn't have an Id mapped

My Entity Class:
public class Building
{
/// <summary>
/// internal Id
/// </summary>
public virtual long Id { get; set; }
..............
}
My Mapping:
var model = AutoMap.AssemblyOf<Building>()
.Setup(s => s.FindIdentity = p => p.Name == "Id")
.Where(t => t.Namespace == "SpikeAutoMappings");
var database = Fluently.Configure()
.Database(DatabaseConfigurer)
.Mappings(m=>m.AutoMappings.Add(model));
I need somebody to help me see what is wrong because I keep having this error when run unit test:
Initialization method TestProject1.MappingTestBase.TestInitialize threw exception. FluentNHibernate.Cfg.FluentConfigurationException: FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
---> FluentNHibernate.Visitors.ValidationException: The entity doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)..
both answers above are right; unless you specify differently, the automapper assumes that you have an int Id field.
If your Id is long, the automapper might not recognize it correctly.
try defining a MappingOverride for your class(es), like so:
public class UserMappingOverride : IAutoMappingOverride<User>
{
#region IAutoMappingOverride<User> Members
public void Override(AutoMapping<User> mapping)
{
mapping.Id(u => u.Name);
}
#endregion
}
the Id() function allows you to override the automapper's convention of what the ID field should be.
for further info on overriding, see http://wiki.fluentnhibernate.org/Auto_mapping#Overrides.
Cheers,
Jhonny
Generally, using AutoMapping is a poor policy because the filed Id must exist in your database tables. Instead, consider using a fluent mapping generator, such as NMG to handle your mapping.
In this case, you would first want to download/install the application, then generate the Mapping Files from your database (Oracle, SQL and various others).
In order to create the Mapping Files, first create an /Entities/ folder within your project. Next, configure the generator software as follows:
Preferences
Generated Property Name = Same as database column name (No change)
Mapping Style = Fluent Mapping
Field or Property = Auto Property
Languages available: C# and VB
Folder : [your project folder]\Entities
Namespace : [your project namespace].Entities
Assembly Name: [your project name].Entities
Next, either Generate All or Generate the Specific Table.
All of the *.cs and *Map.cs files should now be created in your project (you can add them with Add Existing Item... if they don't show up).
Using Fluent, you will see something like the following:
Id(x => x.keyName_ID)
.Column(x => x.keyname_ID)
.GeneratedBy
.Sequence("keyname_ID")
or
Id(x => x.keyName_ID)
.Column(x => x.keyname_ID)
.GeneratedBy
.Identity()
.Column("keyname_ID")
or
Id(x => x.keyName_ID)
.Column(x => x.keyname_ID)
.GeneratedBy
.Assigned()
So, now we need to specify the Id using FluentMapping with Fluent nHibernate. To do this, you need to overwrite the Id line of on code in each of the Map files in the solution. Simply add:
Id(x => x.KeyName_ID)
.GeneratedBy
.GetGeneratorMapping()
.IsSpecified("KeyName_ID");
Where keyname_id is the column name of the id in your database, rather than the one created.
Notice that in your mapping at the BuildSession you must have:
(...).Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<[one of your entities]>()
);
And, now Id is mapped. :) I hope this helps!
My experience with Automapping is that as long as your Entity class has the line:
public virtual int Id { get; private set; }
the automapper will treat it as an ID with no further help from the programmer (i.e. no need for the FindIdenity code you are using in your AutoMap call).
The only difference I see in your ID declaration is that you use a type long instead of int. Don't know if this matters or not.

Set up caching on entities and relationships in Fluent Nhibernate?

Does anyone have an example how to set up and what entities to cache in fluent nhibernate. Both using fluent mapping and auto mapping?
And the same for entity relationships, both one-to-many and many-to-many?
I have been working a a similar situation, where I just want to cache specific elements, and want these elements to be loaded once on start up, and kept in cache, until the application is shut down. This is a read only cache, and is used to populate a list of countries, so that a user can select their country from the list.
I used fluentNhibernate Mappings, and defined Country my class with Cache.readonly()
public class CountryMap : ClassMap<Country> {
public CountryMap() {
Schema("Dropdowns");
Cache.ReadOnly();
// Class mappings underneath
}
}
My user class map looks like this:
public class UserMap : ClassMap<User> {
Id(x => x.Id).Column("UserId");
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Country)
.Column("CountryId");
}
I manually configure Fluent Nhibernate to use Second level cache. So in my fluent Confuguration I have:
var sessionFactory = Fluently.Configure()
.Database (...) // set up db here
.Mappings(...) //set up mapping here
.ExposeConfiguration(c => {
// People advice not to use NHibernate.Cache.HashtableCacheProvider for production
c.SetProperty("cache.provider_class", "NHibernate.Cache.HashtableCacheProvider");
c.SetProperty("cache.use_second_level_cache", "true");
c.SetProperty("cache.use_query_cache", "true");
})
.BuildSessionFactory();
I have checked in SQL profiler, and when I get a list of countrys for a user, the are loaded once, and I get cache hits after every other request. The nice thing is that when displaying the users country name, it loads from the cache, and does not make a request to the database. I got some tips from this posting by Gabriel Schenker. Hope that helps? If you found a better/proper way, please let me know? Thanks!