NHibernate.SimpleMapping

Simple NHibernate mapper using attributes against entity classes and properties.


Keywords
nhibernate, mapping, attributes, database, orm, simple, table
License
MIT
Install
Install-Package NHibernate.SimpleMapping -Version 1.0.20

Documentation

NHibernate.SimpleMapping

This project inspired by need of having no mapping at all similar to Entity Framework functionality. It uses NHibernate.Validator and its own extra attributes to describe the model. The mapping system is not intended to generate the database structure. Instead you must use FluentMigrator. We are aware there is MappingAttributes project NHibernate.Mapping.Attributes but it is not used due to complexity.

The main goal of the project is to simplify the nHibernate mapping process by using attributes against entities and its properties. Apply attributes only if neccessery. You can still use other mapping techniques along with SimpleMapping if you need to.

If you want to be a contributor just create an issue in the project and ask us to add you to the Team ;-).

How to use

Please note all attributes are optional except of [Table] against classes. You can also use [Table] or [Column] attributes to describe one-to-many, many-to-one and many-to-many relations if table/column name is different from default one (Ref + Id).

Default string length is 4000. All nullable properties or classes or strings expect nullable column by default otherwise the column is not nullable. Entities are lazy by default. Properties are not lazy by default. References and Bags are lazy by default.

Feel free to use NHibernate.Cfg.ImprovedNamingStrategy if you are with PostgreSql.

See the extended example bellow:

using NHibernate.SimpleMapping;
using NHibernate.SimpleMapping.Attributes;
using NHibernate.Validator.Constraints;

[Table("contacts")]
public class Contact
{
    // Id column. You can use custom generators like `NHibernate.SimpleMapping.Generators.WebHashValueGeneratorDef` or create your own ones.
    [Key(typeof(NHibernate.Mapping.ByCode.AssignedGeneratorDef))]
    [Ansi]
    [Length(16)]
    public virtual string Id { get; set; }

    // String Length is 150 nvarchar. Column is not nullable.
    [NotNull]
    [Length(150)]
    public virtual string Name { get; set; }

    // Default String Length is 4000 characters. Make property lazy.
    [Lazy]
    public virtual string Description { get; set; }
    
    // This is an ansi string with max length 50 characters. The column in the db must be varchar(50).
    [Ansi]
    [Length(50)]
    public virtual string Uri { get; set; }

    // All virtual properties are mapped based on their type and nullability
    public virtual int Counter { get; set; }

    [UtcDateTime(NoMs = true)]
    public virtual DateTime CreatedDate { get; set; }

    [Date]
    public virtual DateTime DayOfBirth { get; set; }

    // This property won't be mapped as it is not virtual. You will actually have an exception thrown by hibernate if you are using proxies.
    public string Custom1 { get; set; }

    // This property won't be mapped because of attribute [NotMapped]
    [NotMapped]
    public virtual string Custom2 { get; set; }

    // This will be varchar(50) by default because of the enum. You can assign a different string length if you need. No support for numeric enums yet.
    public virtual GenderEnum Gender { get; set; }

    ...
    public enum GenderEnum
    {
        Male,
        Female,
    }
    ...

    // Because bool type is not nullable itself the mapping will be applied as not nullable too. Same for other struct types.
    [Default("false")]
    public virtual bool Archived { get; set; }

    // This property will be readonly.
	[ReadOnly]
	[Formula("name + surname")]
    public virtual string FullName { get { return Name + Surname; } }

    // Reference (many-to-one). By default it expects the column `Contact.AddressId`. You can use [Column("my_custom_column")] if you need to map a different column name.
    [Cascade(Cascade.All | Cascade.DeleteOrphans)]
    public virtual Address Address { get; set; }
    
    // Bag (one-to-many). By default it expects the column `Order.ContactId`. You can use [Column("my_custom_column")] if you need to map a different column name. `ICollection<>` describes Bag. You can also use `IList<>` to describe List or `ISet<>` to describe Set.
    public virtual ICollection<Order> Orders { get; set; }

    // Bag (many-to-many). The attribute [Table] is required. You can also use [Column("my_custom_column", OtherName = "column_from_the_other_side")] if you need to map a different column name.
    [Table("contacts_orders")].
    public virtual ICollection<Order> AllOrders { get; set; }

    // The Order entity mapped in the traditional way (hbm.xml or bycode)
    [NotLazy]
    public virtual Order LastOrder { get; set; }

	// ManyToOne as OneToOne relation because of [Unique]. It requires referenced column RelatedUserId in this table
	[Unique]
    public virtual User RelatedUser { get; set; }

    ...
	[Table("users")]
	public class User
	{
		// OneToOne backend property. Referenced column is on the other side. See Contact.RelatedUser above.
		[OneToOne]
		public virtual Contact RelatedContact { get; set; }
	}
    ...
}

[Table("addresses")]
public class Address
{
    [Key(typeof(NHibernate.Mapping.ByCode.AssignedGeneratorDef))]
    public virtual string Id { get; set; }

    [Length(150)]
    public virtual string Street { get; set; }

    [Length(150)]
    public virtual string City { get; set; }
}

// Mapped in the traditional way (hbm.xml or bycode) but still able to be referenced by SimpleMapping entities. It is not applied to inherited [NotMapped] attributes.
[Table("orders")]
[NotMapped]
public class Order
{
    // Properties are not mapped by `SimpleMapping` attributes because of [NotMapped] against class
    // So you can use any different mapper with this class (like FluentNHibernate or MapByCode).
    // ...
}

// [Discriminator] allows to have many classes in one table. Use [Discriminator("class_type")] to have a different column name.
[Table("dogs")]
[Discriminator]
public abstract class Dog
{
    // ...
}
// So you don't have to worry about [Table] as it is already applied to the base class. It will map a column `discriminator` and put "Doberman" as a discriminated type name. Similar to `SubclassMapping<Doberman>`.
public class Doberman : Dog
{
    // ...
}

// Another example with no discriminator but with table class
[Table("dogs")]
public class Dog
{
    // ...
}
// These are two tables by using `JoinedSubclassMapping`. One table is `dogs` and another is `dobermans`.
[Table("dobermans")]
public class Doberman : Dog
{
    // ...
}

// How to setup the mapping in asp.net.core application
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        var cfg = new Configuration();
        // Some nhibernate config ...
        var mapper = new ModelMapper();
        mapper.AddMappings(MapScanner.Scan(typeof(Startup).Assembly));
        var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
        cfg.AddDeserializedMapping(mapping, "MySimpleMapping");

        services.AddSingleton(cfg);
        services.AddSingleton(s => s.GetRequiredService<Configuration>().BuildSessionFactory());
        services.AddScoped(s => s.GetRequiredService<ISessionFactory>().OpenSession());
    }
}

That's it :-)