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.
Related
I see how I can use the mapping file to specify a custom IUserType for any nhibernate mapped class I want.
However, I don't want to type it in every time. Is there a way to override the standard mapping table seen here?
I have Jørn Schou-Rode's IUserType implementation for storing a guid in Binary(16) in MariaDB. All I want is to enter one or two lines of code to tell Nhibernate when it sees a System.Guid to convert it to the custom "BinaryGuidType" that Schou-Rode made me. Can it be done?
If you are using Fluent NHibernate you can easily do this using Conventions. Here is how I map all strings to varchar instead of nvarchar:
public class PropertyConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
SetStringsAsAnsiStringByDefault(instance);
}
private void SetStringsAsAnsiStringByDefault(IPropertyInstance instance)
{
if (instance.Property.PropertyType == typeof(string))
{
instance.CustomType("AnsiString");
}
else if (instance.Property.PropertyType == typeof(char))
{
instance.CustomType("AnsiChar");
}
}
}
I believe the later versions of NHibernate have in-built support for conventions, but the documentation seems to be sparse. Here is an article for you to get started though: http://weblogs.asp.net/ricardoperes/nhibernate-conventions
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.)
I am retrofitting a very large application to use NHibernate as it's data access strategy. Everything is going well with AutoMapping. Luckily when the domain layer was built, we used a code generator. The main issue that I am running into now is that every collection is hidden behind a custom class that derives from List<>. For example
public class League
{
public OwnerList owners {get;set;}
}
public class OwnerList : AppList<Owner> { }
public class AppList<T> : List<T> { }
What kind of Convention do I have to write to get this done?
I don't think you're going to be able to achieve this with a convention. You will have to create an auto mapping override and then do the following:
mapping.HasMany(l => a.owners).CollectionType<OwnerList>();
When using fluent configuration to specify fluent mappings like this:
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(UserMapping).Assembly))
At the moment I am getting a "NHibernate.MappingException : No persister for" error.
Is it a problem that my Entities and my ClassMaps are in different assemblies? Presumably AddFromAssembly is interested in the assembly that holds the class maps, not the entities? (that is what I have assumed)
Thanks!
UPDATE:
Sorry for not responding to answers very quickly - I had to travel unexpectedly after setting the bounty.
Anyway, thanks for the responses. I've taken a look through them and have updated my code to use AddFromAssemblyOf rather than AddFromAssembly, but still am getting the same error. Possibly I am doing something stupid. Here is the full code for the session factory code I am using:
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var mysqlConfig = FluentNHibernate.Cfg.Db.MySQLConfiguration
.Standard
.ConnectionString("CONNECTION STRING OMITTED")
.UseOuterJoin()
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
_sessionFactory = FluentNHibernate.Cfg.Fluently.Configure()
.Database(mysqlConfig)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
I receive this exception when trying to run a test in nunit that makes use of a repository using this session mechanism:
NHibernate.MappingException : No persister for: xxxx.Model.Entities.User
Thanks
P.S.:
I've tried using both and in AddFromAssemblyOf();
Project with mapping definitions (DataAccess) has reference to project with entities (Model).
What version of Fluent NHibernate are you using? There have been problems with the release candidate and the 1.0 release versions. You may want to consider downloading the latest version from the SVN repository.
http://fluent-nhibernate.googlecode.com/svn/trunk/
Additionally, you may want to check the connection string to make sure that it is completely correct, and you want to make sure that "User" below points to a class.
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<User>())
Also, I should mention that when you use AddFromAssemblyOf, fluent will try to map EVERY class in that assembly. If you have any other classes in that namespace you will want to filter them. There are several different ways to accomplish this. The simplest way is to just place all of the POCOs you want to map in their own namespace and then do something like the following.
.Mappings(m => m.AutoMappings
.Add(AutoMap.AssemblyOf<MyNamespace.Entities.MyClass>()
.Where(type => type.Namespace == "MyNamespace.Entities")
The Where clause will filter items you don't want mapped.
Is it a problem that my Entities and my ClassMaps are in different assemblies?
No there is nothing wrong with that as long as you ClassMap project have refrerence to your Entities project
anyway try this :
m.FluentMappings.AddFromAssemblyOf<UserMapping>()
if this doesn't work post the entire error
Certainly having your entities in a different assembly should not cause a problem as Yassir alludes to above.
According to the Fluent NHibernate Wiki the AddFromAssemblyOf method infers or reflects on the Assembly that contains all of your entities and will map to them when you supply any entity name to it. From the documentation on the FNH wiki you would construct the method as follows:
m.FluentMappings
.AddFromAssemblyOf<YourEntity>();
Therefore in your example, if the entity you are mapping is named User then your code should be constructed as follows:
m.FluentMappings
.AddFromAssemblyOf<User>();
Hope this is of help.
has this been solved? if not could you inlcude your setup?
for example here is my example one
public static ISessionFactory GetSessionFactory()
{
//Old way, uses HBM files only
//return (new Configuration()).Configure().BuildSessionFactory(); //requies the XMl file.
//get database settings.
Configuration cfg = new Configuration();//.Configure();
ft = Fluently.Configure(cfg);
//DbConnection by fluent
ft.Database
(
MsSqlConfiguration
.MsSql2005
.ConnectionString(c => c
.Server(".\\SqlExpress")
.Database("NhibTest")
.TrustedConnection()
)
.ShowSql()
.UseReflectionOptimizer()
);
//set up the proxy engine
//cfg.Properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
//get mapping files.
ft.Mappings(m =>
{
//set up the mapping locations
m.FluentMappings.AddFromAssemblyOf<PersonMap>();//.ExportTo("C:\\mappingfiles");
//m.Apply(cfg);
});
//return the SessionFactory
return ft.BuildSessionFactory();
}
the project structure is as follows
project.Data <- mapping files, and Dao's (also hibernate session manager, containing the above code)
project.Core <- POCO's
project.UI
also have look here incase you have a mixture of Fluent NHibernate and NHibernate configuration
Finally have a look at S#arp Architectures way, as i think it includes this mixture
NhibernateSession <- function : private static ISessionFactory CreateSessionFactoryFor
Hope this helps
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.