Fluent NHibernate Wiki
Explore
Main Page
All Pages
Community
Interactive Maps
Recent Blog Posts
Wiki Content
Recently Changed Pages
Getting started
Auto mapping
Auto-mapping generic base classes
Fluent mapping
Database configuration
Fluent NHibernate Wiki
Main Page
Organization
Help
Forums
Policy
Copyright
Community
Site administration
Site maintenance
Asdasd
Auto-mapping generic base classes
Community
Help
FANDOM
Fan Central
BETA
Games
Anime
Movies
TV
Video
Wikis
Explore Wikis
Community Central
Start a Wiki
Don't have an account?
Register
Sign In
Sign In
Register
Fluent NHibernate Wiki
24
pages
Explore
Main Page
All Pages
Community
Interactive Maps
Recent Blog Posts
Wiki Content
Recently Changed Pages
Getting started
Auto mapping
Auto-mapping generic base classes
Fluent mapping
Database configuration
Fluent NHibernate Wiki
Main Page
Organization
Help
Forums
Policy
Copyright
Community
Site administration
Site maintenance
Asdasd
Auto-mapping generic base classes
Community
Help
Editing
Auto mapping
Back to page
Edit
Edit source
View history
Talk (0)
Edit Page
Auto mapping
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
The edit appears to have already been undone.
Anti-spam check. Do
not
fill this in!
__TOC__ Fluent NHibernate has a concept called Auto Mapping, which is a mechanism for automatically mapping all your entities based on a set of conventions. Auto mapping utilises the principal of [http://en.wikipedia.org/wiki/Convention_over_Configuration convention over configuration]. Using this principal, the auto mapper inspects your entities and makes assumptions of what particular properties should be. Perhaps you have a property with the name of Id and type of <code>int</code>, the auto mapping will decide that this is an auto-incrementing primary key. By using the auto mappings, you can map your entire domain with very little code, and certainly no XML. There are still scenarios where it may not be suitable to use the auto mapping, at which point it would be more appropriate to use the [[Fluent mapping|ClassMap based fluent mappings]]; however, for most greenfield applications (and quite a few brownfield ones too) auto mapping will be more than capable. == Getting started == Lets go through how to map a simple domain using the Fluent NHibernate AutoMapper. Given the following clichéd store domain: <source lang="csharp"> public class Product { public virtual int Id { get; private set; } public virtual string Name { get; set; } public virtual decimal Price { get; set; } } public class Shelf { public virtual int Id { get; private set; } public virtual IList<Product> Products { get; private set; } public Shelf() { Products = new List<Product>(); } } </source> We've got a product, with an auto-incrementing primary key called <code>Id</code>, and <code>Name</code> and <code>Price</code> properties. The store has some shelves it fills with products, so there's a Shelf entity, which has an Id, and a list of the Product's on it; the <code>Product</code> collection is a one-to-many or HasMany relationship to the Product. <blockquote>I'm going to make the assumption here that you have ''an existing NHibernate infrastructure'', if you don't then it's best you consult a general NHibernate walkthrough before continuing.</blockquote> We're going to be using the <code>AutoMap</code> class to do our mapping, which you can use in combination with the [[fluent configuration]] API. To begin with we should take a look at the static <code>AutoMap.AssemblyOf<T></code> method; this method takes a generic type parameter from which we determine which assembly to look in for mappable entities. <source lang="csharp"> AutoMap.AssemblyOf<Product>(); </source> That's it, ''you've mapped your domain''. There are a few customisations you'll want to make before putting this into production though. Lets go through what's actually happening here. The <code>AutoMap.AssemblyOf<Product></code> call creates an instance of an <code>AutoPersistenceModel</code> that's tied to the assembly that Product is declared in. No mappings are actually generated until we push them into NHibernate, this gives us opportunity to customize them before-hand, which we'll go into [[#Conventions|later]]. We're going to utilise the [[fluent configuration]] API to create our <code>SessionFactory</code>. <source lang="csharp"> var sessionFactory = Fluently.Configure() .Database(/* database config */) .Mappings(m => m.AutoMappings .Add(AutoMap.AssemblyOf<Product>())) .BuildSessionFactory(); </source> You can see that our <code>AutoMap</code> is sat in the middle of the configuration, inside the <code>Mappings</code> call; this is our way of telling Fluent NHibernate that we're using the auto mapper; there's nothing stopping you from passing in an instance of <code>AutoPersistenceModel</code> that you've configured elsewhere, we're just doing it in-line for brevity. We're now capable of getting NHibernate to accept our auto mapped entities, there's just one more thing we need to deal with. The auto mapper doesn't know which classes are your entities, and which ones are ''everything else''. The setup we're using above simply maps every class in your assembly as an entity, which isn't going to be very useful; so I'll introduce another method: <code>Where(Func<Type, bool>)</code>. The <code>Where</code> method takes a delegate which is used to limit types based on your own criteria. The most common usage is based on the namespace, but you could also look at the type name, or anything else exposed on the <code>Type</code> instance. <source lang="csharp"> var autoMappings = AutoMap.AssemblyOf<Product>() .Where(t => t.Namespace == "Storefront.Entities"); </source> Bringing all that together leaves us with this NHibernate setup: <source lang="csharp"> var sessionFactory = Fluently.Configure() .Database(/* database config */) .Mappings(m => m.AutoMappings.Add( AutoMap.AssemblyOf<Product>() .Where(t => t.Namespace == "Storefront.Entities"))) .BuildSessionFactory(); </source> Or if you prefer it separate: <source lang="csharp"> var model = AutoMap.AssemblyOf<Product>() .Where(t => t.Namespace == "Storefront.Entities"); var sessionFactory = Fluently.Configure() .Database(/* database config */) .Mappings(m => m.AutoMappings.Add(model)) .BuildSessionFactory(); </source> That's all that you should need. It's all a lot easier than writing out mappings, isn't it? == Identities == The automapper is opinionated, it expects your classes to be designed in a particular manner; if they're not, then it won't be able to automap them without a little assistance. The automapper expects your identities to be named <code>Id</code>, and if they aren't it won't find them. You can modify the way the automapper discovers identities by using the <code>FindIdentity</code> setting. This setting is a lambda that is called with each property of each entity, you just need to return true for any property that matches your identity pattern. <source lang="csharp"> .Setup(s => s.FindIdentity = property => property.Name == property.DeclaringType.Name + "Id"); </source> That example would match any ids that are named after their entity, such as <code>CustomerId</code>. == Components == Sometimes you need components in your domain model, here's how to map them automatically. Lets imagine this database structure: <source lang="sql"> table Person ( Id int primary key, Name varchar(200), AddressNumber int, AddressStreet varchar(100), AddressPostCode varchar(8) ) </source> We want to map that to the following model: <source lang="csharp"> public class Person { public virtual int Id { get; private set; } public virtual string Name { get; set; } public virtual Address Address { get; set; } } public class Address { public int Number { get; set; } public string Street { get; set; } public string PostCode { get; set; } } </source> With this design Address is actually a component, which isn't a full entity, more of a way of providing a clean model to a normalised database structure. Lets start with what we left off with: <source lang="csharp"> AutoMap.AssemblyOf<Person>() .Where(type => type.Namespace.EndsWith("Domain"); </source> We've now got the auto mappings integrated with NHibernate, so we need to instruct the auto mapper how to identify components; after the <code>Where</code> call, we can add a call to <code>WithSetup</code> to help the auto mapper identify our components. <source lang="csharp"> .Setup(s => { s.IsComponentType = type => type == typeof(Address); }) </source> The <code>IsComponentType</code> property we just set is what Fluent NHibernate uses to determine whether a type is one that will be mapped as a component. <blockquote>It's fairly common that you'll have more than one convention type, you'll need to chain together ORs in your <code>IsComponentType</code> lambda, or check on something more generic like the namespace.</blockquote> The Address should now be automatically mapped as a component; the auto mapper will pick up the three properties and map them as properties on the component. If you need to customise the column naming of your component's properties you can set the <code>GetComponentColumnPrefix</code> property: <source lang="csharp"> .Setup(s => { s.IsComponentType = type => type == typeof(Address); s.GetComponentColumnPrefix = type => type.Name + "_"; }) </source> The convention now specifies that columns should be named <code>TypeName_PropertyName</code>, so <code>Address.Street</code> is now mapped to <code>Address_Street</code>. == Conventions == The mappings produced by Fluent NHibernate are based on a pre-defined set of assumptions about your domain, this allows us to greatly reduce the amount of code you're required to write; sometimes however, the conventions we supply are not to your liking, perhaps you're a control-freak and want to have full say over your design, or more likely you're working against an existing database that has its own set of standards. You'd still like to use the auto mapper, but can't because it maps your entities all wrong. Luckily for you we've thought about that, the auto mapper can utilise the [[conventions]] just like fluent mappings. Using the following entities: <source lang="csharp"> public class Product { public int Id { get; private set; } public virtual string Name { get; set; } public virtual decimal Price { get; set; } } public class Shelf { public virtual int Id { get; private set; } public virtual IList<Product> Products { get; private set; } public Shelf() { Products = new List<Product>(); } } </source> The standard conventions, it'd map to a database schema like this: <source lang="sql"> table Product ( Id int identity primary key, Name varchar(100), Price decimal, Shelf_id int foreign key ) table Shelf ( Id int identity primary key ) </source> Nothing too complicated there, but that might not be the schema you expect. Lets pretend (or not!) that you name the primary key after the table it's in, so our Product identity should be called <code>ProductId</code>; also, you like your foreign key's to be explicitly named <code>_FK</code>, and your strings are always a bit longer than 100. Remember this fellow? <source lang="csharp"> AutoMap.AssemblyOf<Product>() .Where(t => t.Namespace == "Storefront.Entities"); </source> Lets update it to include some convention overrides. We'll start with the Id name. The conventions we're about to implement are better explained in the [[conventions]] page. <source lang="csharp"> AutoMap.AssemblyOf<Product>() .Where(t => t.Namespace == "Storefront.Entities") .Conventions.Add<PrimaryKeyConvention>(); </source> We've added a convention to the convention discovery mechanism, now let's implement it. <source lang="csharp"> public class PrimaryKeyConvention : IIdConvention { public void Apply(IIdentityInstance instance) { instance.Column(instance.EntityType.Name + "Id"); } } </source> Our <code>PrimaryKeyConvention</code> gets applied to all Ids and sets their column name based on the entity that contains the Id property. Our primary key's will now be generated as TypeNameId; which means our schema now looks like this: <source lang="sql"> table Product ( ProductId int identity primary key, Name varchar(100), Price decimal, Shelf_id int foreign key ) table Shelf ( ShelfId int identity primary key ) </source> As you can see, our primary key's now have our desired naming convention. Lets do the other two together, as they're so simple; we'll override the foreign-key naming, and change the default length for strings. <source lang="csharp"> .Conventions.Setup(c => { c.Add<PrimaryKeyConvention>(); c.Add<CustomForeignKeyConvention>(); c.Add<DefaultStringLengthConvention>(); }); public class CustomForeignKeyConvention : ForeignKeyConvention { protected override string GetKeyName(PropertyInfo property, Type type) { if (property == null) return type.Name + "_FK"; return property.Name + "_FK"; } } public class DefaultStringLengthConvention : IPropertyConvention { public void Apply(IPropertyInstance instance) { instance.Length(250); } } </source> That's all there is to it, when combined with the other conventions you can customise the mappings quite heavily while only adding a few lines to your auto mapping. This is our final schema: <source lang="sql"> table Product ( ProductId int identity primary key, Name varchar(250), Price decimal, Shelf_FK int foreign key ) table Shelf ( ShelfId int identity primary key ) </source> == Altering entities == Sometimes it's necessary to make slight changes to a specific entity, without wishing to affect anything else; you can do that with the with <code>Override<T></code> method. <source lang="csharp"> .Override<Shelf>(map => { map.HasMany(x => x.Products) .Cascade.All(); }); </source> The <code>Override</code> method takes a generic parameter that's the entity you want to customise. The parameter is an expression that allows you to alter the underlying mapping that is generated by the auto mapper. You can do just about anything in this call that you can do in the [[fluent mapping]]s. In the example above we're setting <code>Cascade.All</code> on the HasMany of Products for the entity Shelf. You can do this for as many types as you need in your domain; however, baring in mind readability, it may sometimes be more appropriate to use an [[#Overrides|override]] or map entities explicitly using the [[fluent mapping]]s if you find yourself overriding a lot of conventions. === Overrides === Using too many calls to <code>Override</code> can quickly clutter up your auto mapping setup; an alternative is to use an <code>IAutoMappingOverride<T></code>, which is an interface you can implement to override the mappings of a particular auto-mapped class. <source lang="csharp"> public class PersonMappingOverride : IAutoMappingOverride<Person> { public void Override(AutoMap<Person> mapping) { } } </source> This example overrides the auto-mapping of a Person entity. Within the <code>Override</code> method you can perform any actions on the mapping that you can in the [[fluent mapping]]s. To use overrides, you need to instruct your AutoMap instance to use them. Typically this would be done in the context of a [[fluent configuration]] setup, but I'll just illustrate with the AutoMap on it's own. <source lang="csharp"> AutoMap.AssemblyOf<Person>() .Where(type => type.Namespace == "Entities") .UseOverridesFromAssemblyOf<PersonMappingOverride>(); </source> It's the <code>UserOverridesFromAssemblyOf<T></code> call that instructs the AutoPersistenceModel to read any overrides that reside the assembly that contains <code>T</code>. === Ignoring properties === When using the auto mapper sometimes you may want to ignore certain properties on your entity. If it's only for one entity, then you can [[#Overrides|override the entity]] or [[#Altering entities|specify an alteration]] each which allow you to call an <code>IgnoreProperty</code> method. For example: <source lang="csharp"> .Override<Shelf>(map => { map.IgnoreProperty(x => x.YourProperty); }); </source> If you need to ignore a property regardless of what type it's present on, you can use the <code>OverrideAll</code> method, which exposes a similar interface. <source lang="csharp"> .OverrideAll(map => { map.IgnoreProperty("YourProperty"); }); </source> The difference in this case is that because we don't know the entity at the time of writing our ignore call, we have to specify the name with a string; this is not ideal, but there aren't really any alternatives in this situation. There are a couple of overloads available when using <code>OverrideAll</code>. A single property: <source lang="csharp"> .OverrideAll(map => { map.IgnoreProperty("YourProperty"); }); </source> Multiple properties: <source lang="csharp"> .OverrideAll(map => { map.IgnoreProperties("YourProperty", "AnotherProperty"); }); </source> Properties by predicate: <source lang="csharp"> .OverrideAll(map => { map.IgnoreProperties(x => x.Name.Contains("Something")); }); </source> That last one is powerful. You can do whatever you like in that predicate against the <code>PropertyInfo</code> instance supplied. You could check if the name contains a particular string, like above, or you could check if the property has a particular attribute on. == Inheritance == There are two main things that you'd want to do with inherited classes, either ignore the base class all together, or map them using an inheritance strategy. I'm going to start with the former, then move on to the latter. === Ignoring base-types === This scenario is where you may have a base class in your domain that you use to simplify your entities, you've moved common properties into it so you don't have to recreate them on every entity; typically this would be the Id and perhaps some audit information. So lets start with a model that has a base class we'd like to ignore. <source lang="csharp"> namespace Entities { public abstract class Entity { public virtual int Id { get; set; } } public class Person : Entity { public virtual string FirstName { get; set; } public virtual string LastName { get; set; } } public class Animal : Entity { public virtual string Species { get; set; } } } </source> Relatively simple model here, we've got an Entity base class that defines the Id, then the Person and Animal entities. We have no desire to have Entity mapped by NHibernate, so we need a way to tell the auto mapper to ignore it. For those individuals from traditional XML mapping land, this is what we're going to be recreating: <source lang="csharp"> <class name="Person"> <id name="Id"> <generator class="identity" /> </id> <property name="FirstName" /> <property name="LastName" /> </class> <class name="Animal"> <id name="Id"> <generator class="identity" /> </id> <property name="Species" /> </class> </source> We'll start with this automapping setup: <source lang="csharp"> AutoMap.AssemblyOf<Entity> .Where(t => t.Namespace == "Entities"); </source> If we were to run this now, we wouldn't get the mapping we desire. Fluent NHibernate would see Entity as an actual entity and map it with Animal and Person as subclasses; this is not what we desire, so we need to modify our auto mapping configuration to reflect that. After <code>AutoMap.AssemblyOf<Entity>()</code> we need to alter the conventions that the auto mapper is using so it can identify our base-class. <source lang="csharp"> AutoMap.AssemblyOf<Entity>() .IgnoreBase<Entity>() .Where(t => t.Namespace == "Entities"); </source> We've added the <code>IgnoreBase<Entity></code> call which simply instructs the automapper to ignore the Entity class; you can chain this call as many times as needed. With this change, we now get our desired mapping. Entity is ignored as far is Fluent NHibernate is concerned, and all the properties (Id in our case) are treated as if they were on the specific subclasses. === Base-type as an inheritance strategy === If you want to have your base-class included in NHibernate, then just don't include the IgnoreBase above! Easy. === Configuring the subclassing strategy === Fluent NHibernate defaults to using table-per-subclass strategy for automapping inheritance hierarchies. If this is not what you want, then you can configure it using the <code>SubclassStrategy</code> configuration option. If you just want every hierarchy to be mapped using the same strategy, then you'd use <code>SubclassStrategy</code> like so: <source lang="csharp"> AutoMap.AssemblyOf<Entity>() .Setup(s => { s.SubclassStrategy = t => SubclassStrategy.Subclass; }); </source> However, if you wanted to customise it on a per-class basis, you'd set it up like so: <source lang="csharp"> AutoMap.AssemblyOf<Entity>() .Setup(s => { s.SubclassStrategy = t => t == typeof(ClassOne) || typeof(ClassTwo); }); </source> === Subclassing FAQs === ==== Abstract base-classes ==== You'll notice that our Entity class is <code>abstract</code>. This is good practice, but for the record, it is ''not'' mandatory. If you're experiencing problems, it's unlikely to be this. In case you're wondering, making the class <code>abstract</code> is like saying "I'll never create an Entity directly, instead I will create derived classes such as Customer and Order (which inherit from Entity)." The default behavior is to consider abstract classes as [http://martinfowler.com/eaaCatalog/layerSupertype.html layer supertypes] and effectively unmapped, you may want to change this for specific scenarios. The easiest way to do this is to use <code>IncludeBase<T></code>, where T is your entity. <source lang="csharp"> AutoMap.AssemblyOf<Entity>() .IncludeBase<AbstractBaseClass>(); </source> This ''forces'' the automapper to include that base-type, regardless of it's previous assumptions about it. ==== Protected setters ==== Sometimes you only want your class to be able to set it's own properties, not the outside world. For example, the Id and CreatedAt properties might never need to be set anywhere but in the class itself. <source lang="csharp"> public abstract Entity { public virtual int Id {get; protected set;} public virtual DateTime CreatedAt { get; protected set;} public Entity() { CreatedAt = DateTime.Now; } } </source> Be sure to make these setters <code>protected</code> rather than <code>private</code>. Doing this will ensure they are included in the Auto Mappings. [[Category:Hjkghkg]]
Summary:
Please note that all contributions to the Fluent NHibernate Wiki are considered to be released under the CC-BY-SA
Cancel
Editing help
(opens in new window)
Follow on IG
TikTok
Join Fan Lab