Private fields in FluentNHibernate - fluent-nhibernate

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");

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.

How to add mappings by namespace in Fluent NHibernate

In my application, I need to talk to multiple databases. I am handling this in NHibernate by creating one SessionFactory per database (I assume this is the correct thing to do). So I have two sets of models (one per database), and two sets of Fluent NHibernate ClassMap<> mappings. Both are in the same project (separated by namespace) and I'd like to keep it that way.
The problem comes when creating the SessionFactory. As far as I can see, Fluent NHibernate has basically two methods for adding mappings:
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserClassMap>())
.Mappings(m => m.FluentMappings.Add<UserClassMap>()
If I use the first overload, then my session factories get all the mappings for both databases. If I use the second, I have to specify each individual ClassMap. I'd like something like FluentMappings.AddFromNamespace(). Is there a way to do this?
It's odd that FluentNHibernate supports this type of filtering for automapping, but not for ClassMaps. It shouldn't be too hard to add this feature yourself, though, via the magic of extension methods. Try this:
public static FluentMappingsContainer AddFromAssemblyOf<T>(
this FluentMappingsContainer mappings,
Predicate<Type> where)
{
if (where == null)
return mappings.AddFromAssemblyOf<T>();
var mappingClasses = typeof(T).Assembly.GetExportedTypes()
.Where(x => (typeof(IMappingProvider).IsAssignableFrom(x)
|| typeof(IExternalComponentMappingProvider).IsAssignableFrom(x))
&& where(x));
foreach (var type in mappingClasses)
{
mappings.Add(type);
}
return mappings;
}
... and use it like this:
m.FluentMappings.AddFromAssemblyOf<UserClassMap>(t => t.Namespace.StartsWith("One.Of.The.Two.Namespaces"));
I wound up writing an extension method that does this for me. Basically it uses reflection to iterate over all the types I'm interested in, and add them one-by-one. It is based on the implementation of AddFromAssemblyOf. Usage:
.Mappings(m => m.FluentMappings.AddFromNamespaceOf<UserClassMap>())
Implementation:
public static class FluentNHibernateExtensions
{
public static FluentMappingsContainer AddFromNamespaceOf<T>(
this FluentMappingsContainer fmc)
{
string ns = typeof(T).Namespace;
IEnumerable<Type> types = typeof(T).Assembly.GetExportedTypes()
.Where(t => t.Namespace == ns)
.Where(x => IsMappingOf<IMappingProvider>(x) ||
IsMappingOf<IIndeterminateSubclassMappingProvider>(x) ||
IsMappingOf<IExternalComponentMappingProvider>(x) ||
IsMappingOf<IFilterDefinition>(x));
foreach(Type t in types) {
fmc.Add(t);
}
return fmc;
}
/// <summary>
/// Private helper method cribbed from FNH source (PersistenModel.cs:151)
/// </summary>
private static bool IsMappingOf<T>(Type type)
{
return !type.IsGenericType && typeof(T).IsAssignableFrom(type);
}
}
Caveats:
The name is a little misleading, since it only searches one assembly. It should perhaps be called AddFromAssemblyAndNamespaceOf, but that's a little verbose.
It is not entirely future-proof. If future versions of FNH add or remove some of the mappable interfaces, it wouldn't include them.
But it works for my purposes.
... AutoMap.AssemblyOf<Person>().Where(x => x.Namespace.EndsWith("Domain")) ...
There is no way to do this. I recommend separating the namespaces out into separate projects. Remember:
Separate namespaces, same project when logical separation makes sense.
Separate namespaces, separate projects when physical separation makes sense.
In this case, since you can't separate by namespace in nhibernate mappings, physical separation makes sense. You can, however, get around this with with fluent automaps that use a .Where or a ShouldMap configuration. Look up fluent automaps and see if that can get you where you want to be.

autopmapping with UseOverridesFromAssemblyOf doesn't call overriding classes of base class

i'm using automapping with fluent nHibernate, very simply, like so:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c
.Server("(local)\\sql2008")
.Database("nHibernate_test")
.TrustedConnection()))
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Domain.Airport>(cfg)
.UseOverridesFromAssemblyOf<ReaderMappingOverride>()
))
my overriding classes are something like that:
public class ReaderMappingOverride : IAutoMappingOverride<Domain.Reader>
{
public void Override(AutoMapping<Domain.Reader> mapping)
{
//use the reader ID as identifier of the class, instead of the ID field defined in superclass Entity
mapping.IgnoreProperty(r => r.Id);
mapping.Id(r => r.ReaderNumber);
}
}
where Reader is an abstract base-class.
if I use a seperate overriding classes for each sub-class it works OK.
Is there any way to define the overriding for all subclasses of the abstract class?
thanks,
Jhonny
ok, just answered my own question-
my problem was that i was trying to map a heirarchy which started with the Reader class, into a single table. but auto-mapping automatically ignores all abstract classes. what i did was just add this to the configuration section:
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Domain.Airport>(cfg)
.IncludeBase<Domain.Reader>()
and this to my configuration class
public override bool IsDiscriminated(Type type)
{
//this line indicates that the readers heirarchy should be all in one table, instead of seperate tables for every type of reader
bool ret = type.IsSubclassOf(typeof(Domain.Reader)) || type == typeof(Domain.Reader) ;
return ret;
}
(BTW, the example given in Fluent nHibernate's site uses the method "type.In(..." which does not exist in .net 3.5...)
that worked fine.
hopes this helps...

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);
});

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.