Automapping doesn't have an Id mapped - fluent-nhibernate

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.

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.

Private fields in FluentNHibernate

I've configured Fluent NHibernate to map the entities in my project. None of my entities expose public properties (except their Id), all of their data is stored in private fields.
By using:
public override bool ShouldMap(Member member)
{
return member.Name == "Id" || (member.IsPrivate && member.IsField);
}
it successfully finds my fields, but then expects my database columns to be called things like _emailAddress.
How can I make it map _emailAddress to a column called EmailAddress? My SessionFactory initialisation looks like:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("AppConnection")))
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<User>(new NHibMappingConfiguration())
.Conventions.Add(DefaultAccess.CamelCaseField(CamelCasePrefix.Underscore))))
.CurrentSessionContext("web")
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
using obviously a default access convention.
Thanks,
Matt
i think you have a couple of options here.
1. you could create your own Convention for naming columns (use the INamingConvention interface. see here).
2. you could create a MappingOverride class for individual classes and define the column name for each one:
mapping.Map(x => x.Name).Column("MyColumnName");

Writing computed properties with NHibernate

I'm using NHibernate 2.1.2 + Fluent NHibernate
I have a ContactInfo class and table. The Name column is encrypted in the database (SQL Server) using EncryptByPassphrase/DecryptByPassphrase.
The following are the relevant schema/class/mapping bits:
table ContactInfo(
int Id,
varbinary(108) Name)
public class ContactInfo
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class ContactInfoMap : ClassMap<ContactInfo>
{
public ContactInfoMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Formula("Convert(nvarchar, DecryptByPassPhrase('passphrase', Name))");
}
}
Using the Formula approach as above, the values get read correctly from the database, but NHibernate doesn't try to insert/update the values when saving to the database (which makes sense).
The problem is that I would like to be able to write the Name value using the corresponding EncryptByPassPhrase function. I'm unsure if NHibernate supports this, and if it does, I haven't been able to find the correct words to search the documentation effectively for it.
So... how can I write this computed property back to the database with NHibernate?
Thanks in advance!
A property mapped to a formula is read-only.
A named query wrapped up in a ContactInfoNameUpdater service might be one way to solve the problem.

Fluent NHibernate mixed mapping properties

I'm trying to use fluent nhibernate to auto map most properties of a class, and then manually map 1 or 2 properties of that same class (without having to manually map all the other variables in the class map).
I have a class with a couple of dozen properties, but one of those properties is a string which needs to be a long length.
Here's an example:
Person class has fields: ID, firstname, lastname, description, and a few dozen other fields.
I would auto map the class but I want 'description' to be a long string, not a nvarchar(255).
So I try:
public class PersonMap : ClassMap
{
public PersonMap()
{
Map(x => x.description).Length(4000);
}
}
but this doesn't auto map all the other properties (an exception is thrown). It's expecting declarations for each property.
Is there a way to accomplish what I'm trying to do?
If anyone needs it, here's the code I'm using to declare the configuration:
FluentConfiguration cfg = Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2000.ConnectionString(Cn));
AutoPersistenceModel mdl =
AutoMap.Assembly(System.Reflection.Assembly.GetExecutingAssembly());
cfg.Mappings(m => m.AutoMappings.Add(mdl.Where(type =>
type.Namespace != null && type.Namespace.ToLower() == strNamespace.ToLower() )));
Thanks!
Ok I figured it out. There's a method called 'override' that I can use when declaring the configuration, and in there I can specify all the overrides for specific properties:
AutoPersistenceModel mdl = AutoMap.Assembly(System.Reflection.Assembly.GetExecutingAssembly());
mdl.Override<MyNamespace.Person>(map =>
{
map.Map(x => x.description).Length(4000);
});

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

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.