Fluent NHibernate: override derived classes not in the base class auto-mapping - fluent-nhibernate

The story:
I had class User and class Organization: User. I did not use any mappings for these classes, let FNH do mapping automatically. Then, I added
public class OrganizationMap : IAutoMappingOverride<Organization>
{
public void Override(AutoMap<Organization> mapping)
{
}
}
Notice there're no overrides. So I did not expect any changes in FNH behavior. But I got this (during schema export actually):
NHibernate.MappingException:
(XmlDocument)(2,4): XML validation
error: The element 'class' in
namespace 'urn:nhibernate-mapping-2.2'
has incomplete content. List of
possible elements expected: 'meta,
subselect, cache, synchronize,
comment, tuplizer, id, composite-id'
in namespace
'urn:nhibernate-mapping-2.2'.
The generated Orders.Core.Organization.hbm.xml was really empty:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="">
<class name="Orders.Core.Organization, Orders.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Organizations" xmlns="urn:nhibernate-mapping-2.2" />
</hibernate-mapping>
So, after I reviewed the User.hbm I got the idea - I need to override Organization in the base class like this:
public class UserMap : IAutoMappingOverride<User>
{
public void Override(AutoMap<User> mapping)
{
mapping.JoinedSubClass<Organization>("ColumnId", m => {...}
);
}
}
But, I would better like to do this in a separate mapping override class for Organization... after all, what would the mapping become if I have 5 subclasses all in single Override method.
Is this possible?

Your override is telling FNH that you will manually write the mappings for that class. The error you are getting is because there is nothing being mapped for Organisation (if you look at the generated HBM.xml it will be empty).
What exactly are you wanting to write the override for?
Edit:
In that case, you can do something like this:
public class MyAlteration : IAutoMappingAlteration
{
public void Alter(AutoPersistenceModel model)
{
model.ForTypesThatDeriveFrom<User>(
map => map.HasMany<User>( x => x.Children)
);
}
}
And when configuring fluent nhibernate:
model.Alteration( a => a.Add<MyAlteration>());
Note: This is using the latest codebase of fluent nhibernate (1.0RC).

Turned out that with latest FNH (some revision after RC) this is possible now. I wonder if this is because I asked ;-)
So I had this
mapping.JoinedSubClass<Organization>("UserId", m =>
{
m.HasMany(x => x.Currencies).Element("Currency").AsBag();
}
);
and it stopped working after upgrading to RC. Then I moved this into its own class
public class OrganizationMap : IAutoMappingOverride<Organization>
{
public void Override(AutoMapping<Organization> mapping)
{
mapping.HasMany(x => x.Currencies).Element("Currency").AsBag();
}
}
it started to work again. Just like I wanted! Now I don't even need to indicate JoinedSubClass as this is the default, anyway. I can just override my subclass properties which is cool.
Though it wasn't too easy to figure out why NH started to complain about association of strings... I even thought that .Element is broken in RC. I wonder why JoinedSubClass still has this mapping part if it doesn't completely work.

Related

Fluent NHibernate does not call my ClassMap<ControlEntity> class

I use NHibernate for a dynamic website that its modules can be loaded dynamically, so when I want to build a sessionFactory, I use a way to find all assemblies and sort them with their dependencies
after all, I add them to Configuration instance I created and it works.
Now I want to change configuration type from hbm.xml files to fluent
I added below codes:
sessionFactory =
Fluently
.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ConnectionString(
c => c.FromAppSetting("connectionString")
)
)
.Mappings(
m => m.AutoMappings.Add(
AutoMap.Assemblies(
new FarayanConfig(),
assembliesArray
)
)
).BuildSessionFactory();
FarayanConfig is:
class FarayanConfig : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Name.EndsWith("Entity");
}
public override bool IsVersion(FluentNHibernate.Member member)
{
return member.Name == "Version";
}
}
also I have a class in an assembly that will be loaded by this code (notice that assembly is not referenced, will be loaded dynamically) with a class named ControlEntity and also another class:
public class ControlEntityMap : ClassMap<ControlEntity>
{
public ControlEntityMap()
{
HasMany(x => x.Properties).Component(c => {
c.Map(v => v.Culture);
c.Map(v => v.Name);
c.Map(v => v.Value);
});
}
}
now the problem is constructor of ControlEntityMap will not execute!
what I must do?
Because of you are trying to use AutoMap.
You can use something like this:
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("your assembly name")))
Update:
You are doing right by ovverride DefaultAutomappingConfiguration for this situation but also you are trying to AutoMap all classes which ones end with "Entity" and your class which one you want to ignore it from AutoMap also ends with "Entity". I think you can seperate your classes in different namespaces and declare it in your ShouldMap property.
And there are some information in FluentNhibenate Wiki:
You can ignore base types by simply excluding them from your
ShouldMap(Type) method, that's sometimes the cleanest option; however,
if you want to be a bit more explicit you can use the IgnoreBase
method.
After AutoMap.AssemblyOf() we need to alter the conventions
that the auto mapper is using so it can identify our base-class.
AutoMap.AssemblyOf(cfg) .IgnoreBase();
We've added the IgnoreBase call which simply instructs the
automapper to ignore the Entity class; you can chain this call as many
times as needed.

Custom Fluent NHibernate maps not working with AutoMapping

I'm having an issue with Fluent NHibernate AutoPersistenceModelGenerator. It doesn't want to pick up custom maps.
Using Sharp Architecture 2.0, Fluent NHibernate 1.2 and NHibernate 3.1.
My current relevant configuration is as follows:
public AutoPersistenceModel Generate()
{
// This mappings group works with the exception of custom maps!!
var mappings = AutoMap.AssemblyOf<SecurableEntity>(new AutomappingConfiguration());
mappings.Conventions.Setup(GetConventions());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase<SecurableEntity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
//mappings.UseOverridesFromAssemblyOf<UserMap>(); // Should call Override method of UserMap, but doesn't appear to...
mappings.Override<User>(new UserMap().Override()); // This hack fixes the issue with calling the Override method of UserMap.
mappings.UseOverridesFromAssemblyOf<UserMap>();
return mappings;
}
class UserMap : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
//mapping => mapping.Table("Users");
mapping.Table("Users");
}
public Action<AutoMapping<User>> Override()
{
return map =>
{
map.Table("Users");
};
}
}
I've tried making various modifications to the configuration and pouring over internet articles on Fluent NHibernate, to no avail. I have a working version using Sharp Arch 1.x, and earlier versions of NHibernate and Fluent. I'm assuming that there has been a change in syntax that I'm missing. Any and all help would be greatly appreciated.
Thank you!
John
Fluent NHibernate use Assembly.GetExportedTypes() method to get all the overrides from given assembly. As this method's documentation say, it gets the public types defined in this assembly that are visible outside the assembly. Your override is implicitly internal. Just add public before class UserMap and it'll work.

Can auto mappings conventions work with mapping overrides?

I have a convention for my ids, which automatically maps properties with a name of Id as the identifier. As requirements are being fleshed out I need to tweak a domain model so naturally I went online and found that I need to create a class that inherits from IAutoMappingOverride<T>.
My convention:
public class PrimaryKeyConvention : IIdConvention, IIdConventionAcceptance
{
public void Apply(IIdentityInstance instance)
{
instance.Column("Id");
instance.GeneratedBy.SeqHiLo(instance.Name, "10");
}
public void Accept(IAcceptanceCriteria<IIdentityInspector> criteria)
{
criteria.Expect(x => x.Generator, Is.Not.Set);
}
}
My override:
public class LocateMappingOverride : IAutoMappingOverride<Locate>
{
public void Override(AutoMapping<Locate> mapping)
{
mapping.Map(x => x.SendTo).Not.Nullable();
}
}
The convention does work as expected if I remove my override.
The exception I get is The entity 'LocateMappingOverride' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)..
Is it possible to use conventions in conjunction with mapping overrides?
The answer is - yes, automapping can work with overrides.
Look what the error said. The problem is not with Locate entity, but with LocateMappingOverride entity, and that class should not be treated as entity, of course. You must have IAutomappingConfiguration configured so that FluentNHibernate's rule what to treat as entity includes LocateMappingOverride, too. And it does not have an Id mapped, indeed.
You should either:
change your IAutomappingConfiguration so that classes that implements IAutoMappingOverride<> are excluded
move the override outside the scope that is searched for entities
or introduce a common marker interface that all entities need to implement, i.e. IEntity and change IAutomappingConfiguration rules respectively.

Fluent NHibernate - Setting CutomType IIdConvention

I have the following IIdConvention for a FluentNHibernate automapping. I want all of my id properties to use a custom type that is represented by a string property but the CustomType is never applied to my mappings.
public class PrimaryKeyHasTableName : FluentNHibernate.Conventions.IIdConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Id");
instance.CustomType<CustomIdType>();
}
}
When I looked into the FluentNHibernate source it appears that the Type for the id property has already been set so it is not being set by my convention.
If I use a ClassMap to map the class manually I have not problem setting the CustomType for the Identity property.
Id(x => x.Id)
.Column("UserId")
.CustomType<OnFileIdType>();
Does anybody know how I can successfully set the custom id property using a convention?
Or get my convention to run earlier in the mapping process so that the Type isn't already set by the time my code runs.
Also, here's my configuration code:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(connString))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<BaseEntity>();
m.AutoMappings.Add(AutoMap.AssemblyOf<BaseEntity>()
.Where(t => t.Namespace.EndsWith("Models.Domain"))
.Conventions.AddFromAssemblyOf<BaseEntity>()
.UseOverridesFromAssemblyOf<BaseEntity>()
);
})
.ExposeConfiguration(CreateSchema)
.BuildSessionFactory();
Thanks.
I don't think you can achieve what you want with conventions.
One thing you can try is having all your entities subclass from an abstract entity that defines the Id property and has the custom type mapping for it.
I wouldn't recommend this however, It will just open room for more automapping problems.
Just go for the manual CustomType for each class map.
I'm having the exact same problem, it would be great if FNH's IIdConvention supported this.
After trying a few things and reading this I have resigned myself to implementing IAutoMappingOverride for entities using a custom type for their Id.
public class ProductMap : IAutoMappingOverride<Product>
{
public void Override(AutoMapping<Product> mapping)
{
mapping.Id(x => x.Id).CustomType<ProductId>();
}
}
Using automapping override instead of ClassMap will continue to automap the rest of your properties. Alter your AutoMappings initialisation code to include
.Overrides.AddFromAssemblyOf<BaseEntity>()
I am getting the same problem. I believe that it is arising from the invocation of Conventions.AddFromAssemblyOf<BaseEntity>(), which is causing your custom convention to not be applied.
(By the way, the posted code will work only if your convention classes are in the same assembly as your BaseEntity - it might be safer to use Conventions.AddFromAssemblyOf<PrimaryKeyHasTableName>())
A bug was raised about AddFromAssemblyOf back in February 2011: Fluent NHibernate - Setting CutomType IIdConvention. There is no resolution posted.
The work-around appears to be to add each of the Conventions explicitly
Conventions.Add<PrimaryKeyHasTableName>()
To check this, you can add the following line after the line which adds the automappings, in order to export the mappings to an hbm.xml file on your hard drive, and take a look at the generated mappings.
m.AutoMappings.ExportTo(#"C:\ExportedMappings")
Also, I suggest you add a breakpoint into the Apply method and run the code to ensure that it is being invoked.)

Fluent nHibernate with BindingList<T>

nHibernate is giving the error : Custom type does not implement UserCollectionType: myApp.Domain.OrderLineCollection.
BindingList implements IList, so why is nHibernate trying to use UserCollectionType instead of IList?
public class OrderHeader
{
public virtual int OrderHeaderId { get; set; }
public virtual string OrderNumber { get; set; }
public virtual OrderLineCollection Line { get; set; }
}
public class OrderLineCollection : BindingList<OrderHeader> { }
public class OrderHeaderMap : ClassMap<OrderHeader>
{
public OrderHeaderMap()
{
WithTable("Orders");
Id(x => x.OrderHeaderId, "OrderId").GeneratedBy.Identity();
Map(x => x.OrderNumber);
HasMany(x => x.Line).WithKeyColumn("OrderHeaderId").AsList();
}
}
<list name="Line">
<key column="OrderHeaderId" />
<index />
<one-to-many class="myApp.Domain.OrderLine, myApp.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</list>
NHibernate has it's own custom typed list which implements IList underneath.
I'm afraid you won't be able to use yours without creating nHibernate UserType.
But i might be wrong and would be glad to hear why. :)
You might want to check the XML that's created by fluentNHibernate - it's quite possible they take the type of the Line property and set it explicitly.
This should work if you don't set the type explicitly. I tried implementing a custom collection deriving from IList - and it worked when I didn't specify the type on the bag/list whatever in the mapping.
Ok, I did a quick test Arnis L. is right - it probably won't work without implementing UserCollectionType. In my experience, it's a pain to implement .
(somehow I remembered doing something like this but I guess my mind's playing tricks on me)
I look at the NHibernate source code and at least for PersistentBag and PersistentList NHibernate will instanciate a ArrayList object as the back end list, not a OrderLineCollection as one could thought. When you implement IUserColletionType there is a method who tells NHibernate what collection it should create, and also what Persistent collection Hibernate should use to sav. Take a look at this link might help a lot. But I still cant do Nhibernate work with BindingList.