"No persister for" error with NHibernate, NHibernate.Linq and Fluent Mapping - nhibernate

I´m using Nhibernate 2.1.2.4000 GA with Nhibernate.Linq 1.0 and latest version of FluentNhibernate downloaded from master on github.
Im doing some tests and whenever I try to delete a entity retrieved by a linq query i´m getting this error:
No persister for: NHibernate.Linq.Query`1[[Employees.Core.Entities.Employee, Employees.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
All other operations (insert, update and select) looks fine;
My Entity class:
public class Employee
{
public Employee()
{
}
public virtual Int32 Id { get; private set; }
public virtual String Name { get; set; }
public virtual String SayHello()
{
return String.Format("'Hello World!', said {0}.", Name);
}
}
Mapping class:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Not.Nullable()
.Length(50);
}
}
Configuration:
Assembly mappingsAssemly = Assembly.GetExecutingAssembly();
return Fluently.Configure()
.Database( MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.ProxyFactoryFactory(typeof(ProxyFactoryFactory))
.ShowSql())
.Mappings( m => m.FluentMappings.AddFromAssembly(mappingsAssemly))
.BuildSessionFactory();
And the code that does not work:
public void RemoveAll()
{
var q = from employee in _session.Linq<Employee>()
select employee;
foreach (var employee in q.ToList())
{
_session.Delete(q);
}
}
Any thoughts?

We all missed it!
Sorry guys and thanks for all your help but I just figured.
If you pay attention to the RemoveAll() method you will see that I´m trying to delete the "q" object which is not an entity, but a IQueriable instead of passing "employee". It was lack of attention.
The right code would be:
public void RemoveAll()
{
var q = from employee in _session.Linq()
select employee;
var p = q.ToList();
foreach (var employee in p)
{
_session.Delete(employee);
}
}

Are you certain the assembly you're supplying to FNH (Assembly.GetExecutingAssembly()) is actually the one containing your mappings?
Modify your Mappings call to include the ExportTo method, which'll export any mappings FNH finds to a specified folder; check out the contents of that folder and see if all the mappings are in there. If they are, then chances are it's not a FNH issue and might be a problem with the Linq provider (as Michael said).
Mappings(
m => m.FluentMappings
.AddFromAssembly(mappingsAssemly)
.ExportTo(#"C:\"));
Another thing you can check is the NHibernate Configuration instance that NH is actually using. To do that, use BuildConfiguration instead of BuildSessionFactory and inspect the result; there's a ClassMappings collection (or some variation of that), which should contain all the mapped entities.
If that looks fine, then try creating your query using the Criteria API or HQL instead, see if that fixes your problem (and in that case it's almost certain to be the linq provider).

It may just be a bug in the Linq provider. You may want to try to reproduce the problem against the latest NHibernate trunk which includes a new/different Linq provider. Alternatively, if you (successfully) remove Fluent NHibernate from the equation, you can probably (relatively easily) submit a test case / bug report against the Linq provider.

Related

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.

How do you automap List<float> or float[] with Fluent NHibernate?

Having successfully gotten a sample program working, I'm now starting
to do Real Work with Fluent NHibernate - trying to use Automapping on my project's class
heirarchy.
It's a scientific instrumentation application, and the classes I'm
mapping have several properties that are arrays of floats e.g.
private float[] _rawY;
public virtual float[] RawY
{
get
{
return _rawY;
}
set
{
_rawY = value;
}
}
These arrays can contain a maximum of 500 values.
I didn't expect Automapping to work on arrays, but tried it anyway,
with some success at first. Each array was auto mapped to a BLOB
(using SQLite), which seemed like a viable solution.
The first problem came when I tried to call SaveOrUpdate on the
objects containing the arrays - I got "No persister for float[]"
exceptions.
So my next thought was to convert all my arrays into ILists e.g.
public virtual IList<float> RawY { get; set; }
But now I get:
NHibernate.MappingException: Association references unmapped class: System.Single
Since Automapping can deal with lists of complex objects, it never
occured to me it would not be able to map lists of basic types. But
after doing some Googling for a solution, this seems to be the case.
Some people seem to have solved the problem, but the sample code I
saw requires more knowledge of NHibernate than I have right now - I
didn't understand it.
Questions:
1. How can I make this work with Automapping?
2. Also, is it better to use arrays or lists for this application?
I can modify my app to use either if necessary (though I prefer
lists).
Edit:
I've studied the code in Mapping Collection of Strings, and I see there is test code in the source that sets up an IList of strings, e.g.
public virtual IList<string> ListOfSimpleChildren { get; set; }
[Test]
public void CanSetAsElement()
{
new MappingTester<OneToManyTarget>()
.ForMapping(m => m.HasMany(x => x.ListOfSimpleChildren).Element("columnName"))
.Element("class/bag/element").Exists();
}
so this must be possible using pure Automapping, but I've had zero luck getting anything to work, probably because I don't have the requisite knowlege of manually mapping with NHibernate.
Starting to think I'm going to have to hack this (by encoding the array of floats as a single string, or creating a class that contains a single float which I then aggregate into my lists), unless someone can tell me how to do it properly.
End Edit
Here's my CreateSessionFactory method, if that helps formulate a
reply...
private static ISessionFactory CreateSessionFactory()
{
ISessionFactory sessionFactory = null;
const string autoMapExportDir = "AutoMapExport";
if( !Directory.Exists(autoMapExportDir) )
Directory.CreateDirectory(autoMapExportDir);
try
{
var autoPersistenceModel =
AutoMap.AssemblyOf<DlsAppOverlordExportRunData>()
.Where(t => t.Namespace == "DlsAppAutomapped")
.Conventions.Add( DefaultCascade.All() )
;
sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile)
.ShowSql()
)
.Mappings(m => m.AutoMappings.Add(autoPersistenceModel)
.ExportTo(autoMapExportDir)
)
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory()
;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return sessionFactory;
}
I would probably do a one to many relationship and make the list another table...
But maybe you need to rethink your object, is there also a RawX that you could compose into a RawPoint? This would make a table with 3 columns (ParentID, X, Y).
The discontinuity comes from wanting to map a List to a value that in an RDBMS won't go in a column very neatly. A table is really the method that they use to store Lists of data.
This is the whole point of using an ORM like NHibernate. When doing all the querying and SQL composition by hand in your application, adding a table had a high cost in maintenance and implementation. With NHibernate the cost is nearly 0, so take advantage of the strengths of the RDBMS and let NHibernate abstract the ugliness away.
I see your problem with mapping the array, try it with an override mapping first and see if it will work, then you could maybe create a convention override if you want the automap to work.
.Override<MyType>(map =>
{
map.HasMany(x => x.RawY).AsList();
})
Not sure if that will work, I need to get an nHibernate testing setup configured for this stuff.
Since I posted my question, the Fluent NHibernate team have fixed this problem.
You can now automap ILists of C# value types (strings, ints, floats, etc).
Just make sure you have a recent version of FNH.
Edit
I recently upgraded from FNH 1.0 to FNH 1.3.
This version will also automap arrays - float[], int[], etc.
Seems to map them as BLOBs. I assume this will be more efficient than ILists, but have not done any profiling to confirm.
I eventually got an override to work - see the end of the code listing. The key points are:
a new mapping class called DlsAppOverlordExportRunDataMap
the addition of a UseOverridesFromAssemblyOf clause in
CreateSessionFactory
Also, it turns out that (at least with v. 1.0.0.594) there is a very big gotcha with Automapping - the mapping class (e.g. DlsAppOverlordExportRunDataMap) cannot be in the same Namespace as the domain class (e.g. DlsAppOverlordExportRunData)!
Otherwise, NHibernate will throw "NHibernate.MappingException: (XmlDocument)(2,4): XML validation error: ..." , with absolutely no indication of what or where the real problem is.
This is probably a bug, and may be fixed in later versions of Fluent NHibernate.
namespace DlsAppAutomapped
{
public class DlsAppOverlordExportRunData
{
public virtual int Id { get; set; }
// Note: List<float> needs overrides in order to be mapped by NHibernate.
// See class DlsAppOverlordExportRunDataMap.
public virtual IList<float> RawY { get; set; }
}
}
namespace FrontEnd
{
// NEW - SET UP THE OVERRIDES
// Must be in different namespace from DlsAppOverlordExportRunData!!!
public class DlsAppOverlordExportRunDataMap : IAutoMappingOverride<DlsAppOverlordExportRunData>
{
public void Override(AutoMapping<DlsAppOverlordExportRunData> mapping)
{
// Creates table called "RawY", with primary key
// "DlsAppOverlordExportRunData_Id", and numeric column "Value"
mapping.HasMany(x => x.RawY)
.Element("Value");
}
}
}
private static ISessionFactory CreateSessionFactory()
{
ISessionFactory sessionFactory = null;
const string autoMapExportDir = "AutoMapExport";
if( !Directory.Exists(autoMapExportDir) )
Directory.CreateDirectory(autoMapExportDir);
try
{
var autoPersistenceModel =
AutoMap.AssemblyOf<DlsAppOverlordExportRunData>()
.Where(t => t.Namespace == "DlsAppAutomapped")
// NEW - USE THE OVERRIDES
.UseOverridesFromAssemblyOf<DlsAppOverlordExportRunData>()
.Conventions.Add( DefaultCascade.All() )
;
sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile)
.ShowSql()
)
.Mappings(m => m.AutoMappings.Add(autoPersistenceModel)
.ExportTo(autoMapExportDir)
)
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory()
;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return sessionFactory;
}
Didn't get any answers here or on the Fluent NHibernate mailing list that actually worked, so here's what I did.
It smells like a horrible hack, but it works. (Whether it will scale up to large data sets remains to be seen).
First, I wrapped a float property (called Value) in a class:
// Hack - need to embed simple types in a class before NHibernate
// will map them
public class MappableFloat
{
public virtual int Id { get; private set; }
public virtual float Value { get; set; }
}
I then declare the properties in other classes that need to be Lists of floats e.g.
public virtual IList<MappableFloat> RawYMappable { get; set; }
NHibernate creates a single database table, with multiple foreign keys, e.g.
create table "MappableFloat" (
Id integer,
Value NUMERIC,
DlsAppOverlordExportRunData_Id INTEGER,
DlsAppOverlordExportData_Id INTEGER,
primary key (Id)
)

Fluent mapping - entities and classmaps in different assemblies

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

NHibernate - Do I have to have a class to interface with a table?

I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.

Generate table indexes using Fluent NHibernate

Is it possible to generate table indexes along with the rest of the database schema with Fluent NHibernate? I would like to be able to generate the complete database DDL via an automated build process.
In more recent versions of Fluent NHibernate, you can call the Index() method to do this rather than using SetAttribute (which no longer exists):
Map(x => x.Prop1).Index("idx__Prop1");
Do you mean indexes on columns?
You can do it manually in your ClassMap<...> files by appending .SetAttribute("index", "nameOfMyIndex"), e.g. like so:
Map(c => c.FirstName).SetAttribute("index", "idx__firstname");
or you can do it by using the attribute features of the automapper - e.g. like so:
After having created your persistence model:
{
var model = new AutoPersistenceModel
{
(...)
}
model.Conventions.ForAttribute<IndexedAttribute>(ApplyIndex);
}
void ApplyIndex(IndexedAttribute attr, IProperty info)
{
info.SetAttribute("index", "idx__" + info.Property.Name");
}
and then do this to your entities:
[Indexed]
public virtual string FirstName { get; set; }
I like the latter. Is is a good compromise between not being non-instrusive to your domain model, yet still being very effective and clear on what is happening.
Mookid's answer is great and helped me a lot, but meanwhile the ever evolving Fluent NHibernate API has changed.
So, the right way to write mookid sample now is the following:
//...
model.ConventionDiscovery.Setup(s =>
{
s.Add<IndexedPropertyConvention>();
//other conventions to add...
});
where IndexedPropertyConvention is the following:
public class IndexedPropertyConvention : AttributePropertyConvention<IndexedAttribute>
{
protected override void Apply(IndexedAttribute attribute, IProperty target)
{
target.SetAttribute("index", "idx__" + target.Property.Name);
}
}
The [Indexed] attribute works the same way now.